Merge branch 'beta' into desolate-land-bug
This commit is contained in:
commit
3ae2ecac6e
|
@ -56,7 +56,7 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
|
||||||
- Pokémon Legends: Arceus
|
- Pokémon Legends: Arceus
|
||||||
- Pokémon Scarlet/Violet
|
- Pokémon Scarlet/Violet
|
||||||
- Firel (Custom Ice Cave, Laboratory, Metropolis, Plains, Power Plant, Seabed, Space, and Volcano biome music)
|
- Firel (Custom Ice Cave, Laboratory, Metropolis, Plains, Power Plant, Seabed, Space, and Volcano biome music)
|
||||||
- Lmz (Custom Jungle biome music)
|
- Lmz (Custom Ancient Ruins, Jungle, and Lake biome music)
|
||||||
- Andr06 (Custom Slum and Sea biome music)
|
- Andr06 (Custom Slum and Sea biome music)
|
||||||
|
|
||||||
### 🎵 Sound Effects
|
### 🎵 Sound Effects
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -5,29 +5,29 @@
|
||||||
"format": "RGBA8888",
|
"format": "RGBA8888",
|
||||||
"size": {
|
"size": {
|
||||||
"w": 78,
|
"w": 78,
|
||||||
"h": 87
|
"h": 86
|
||||||
},
|
},
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"frames": [
|
"frames": [
|
||||||
{
|
{
|
||||||
"filename": "0001.png",
|
"filename": "0001.png",
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"trimmed": true,
|
"trimmed": false,
|
||||||
"sourceSize": {
|
"sourceSize": {
|
||||||
"w": 80,
|
"w": 78,
|
||||||
"h": 87
|
"h": 86
|
||||||
},
|
},
|
||||||
"spriteSourceSize": {
|
"spriteSourceSize": {
|
||||||
"x": 1,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"w": 78,
|
"w": 78,
|
||||||
"h": 87
|
"h": 86
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"w": 78,
|
"w": 78,
|
||||||
"h": 87
|
"h": 86
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -36,6 +36,6 @@
|
||||||
"meta": {
|
"meta": {
|
||||||
"app": "https://www.codeandweb.com/texturepacker",
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
"smartupdate": "$TexturePacker:SmartUpdate:d3cce87ee0e3a880d840bffe9373d5d4:7c776d33b75abad1fe36b14a5e5734af:56468b7a2883e66dadcd2af13ebd8010$"
|
"smartupdate": "$TexturePacker:SmartUpdate:65266da62e9d2953511c0d68ae431345:c1ca63690bed8dd5af71bb443910c830:56468b7a2883e66dadcd2af13ebd8010$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "expert_pokemon_breeder.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 39,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 21,
|
||||||
|
"y": 3,
|
||||||
|
"w": 39,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 39,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:cb681265d8dca038a518ab14076fd140:18ff41b1ef6967682643a11695926e58:c59ea3971195f5a395b75223a77d9068$"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1006 B |
|
@ -1911,6 +1911,19 @@ export default class BattleScene extends SceneBase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fades out current track for `delay` ms, then fades in new track.
|
||||||
|
* @param newBgmKey
|
||||||
|
* @param destroy
|
||||||
|
* @param delay
|
||||||
|
*/
|
||||||
|
fadeAndSwitchBgm(newBgmKey: string, destroy: boolean = false, delay: number = 2000) {
|
||||||
|
this.fadeOutBgm(delay, destroy);
|
||||||
|
this.time.delayedCall(delay, () => {
|
||||||
|
this.playBgm(newBgmKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
playSound(sound: string | AnySound, config?: object): AnySound {
|
playSound(sound: string | AnySound, config?: object): AnySound {
|
||||||
const key = typeof sound === "string" ? sound : sound.key;
|
const key = typeof sound === "string" ? sound : sound.key;
|
||||||
config = config ?? {};
|
config = config ?? {};
|
||||||
|
@ -2157,6 +2170,16 @@ export default class BattleScene extends SceneBase {
|
||||||
return 13.13;
|
return 13.13;
|
||||||
case "battle_macro_boss": //SWSH Rose Battle
|
case "battle_macro_boss": //SWSH Rose Battle
|
||||||
return 11.42;
|
return 11.42;
|
||||||
|
case "mystery_encounter_gen_5_gts": // BW GTS
|
||||||
|
return 8.52;
|
||||||
|
case "mystery_encounter_gen_6_gts": // XY GTS
|
||||||
|
return 9.24;
|
||||||
|
case "mystery_encounter_fun_and_games": // EoS Guildmaster Wigglytuff
|
||||||
|
return 4.78;
|
||||||
|
case "mystery_encounter_weird_dream": // EoS Temporal Spire
|
||||||
|
return 41.42;
|
||||||
|
case "mystery_encounter_delibirdy": // Firel Delibirdy
|
||||||
|
return 82.28;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2603,7 +2626,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
|
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
|
||||||
if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i] && heldModifiersConfigs[i].length > 0) {
|
if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i]) {
|
||||||
heldModifiersConfigs[i].forEach(mt => {
|
heldModifiersConfigs[i].forEach(mt => {
|
||||||
let modifier: PokemonHeldItemModifier;
|
let modifier: PokemonHeldItemModifier;
|
||||||
if (mt.modifier instanceof PokemonHeldItemModifierType) {
|
if (mt.modifier instanceof PokemonHeldItemModifierType) {
|
||||||
|
@ -2614,8 +2637,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
const stackCount = mt.stackCount ?? 1;
|
const stackCount = mt.stackCount ?? 1;
|
||||||
modifier.stackCount = stackCount;
|
modifier.stackCount = stackCount;
|
||||||
// TODO: set isTransferable
|
modifier.isTransferable = mt.isTransferable ?? modifier.isTransferable;
|
||||||
// modifier.isTransferrable = mt.isTransferable ?? true;
|
|
||||||
this.addEnemyModifier(modifier, true);
|
this.addEnemyModifier(modifier, true);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
|
||||||
export enum BattleType {
|
export enum BattleType {
|
||||||
|
@ -157,7 +157,7 @@ export default class Battle {
|
||||||
}
|
}
|
||||||
|
|
||||||
addPostBattleLoot(enemyPokemon: EnemyPokemon): void {
|
addPostBattleLoot(enemyPokemon: EnemyPokemon): void {
|
||||||
this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => {
|
this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => {
|
||||||
const ret = i as PokemonHeldItemModifier;
|
const ret = i as PokemonHeldItemModifier;
|
||||||
//@ts-ignore - this is awful to fix/change
|
//@ts-ignore - this is awful to fix/change
|
||||||
ret.pokemonId = null;
|
ret.pokemonId = null;
|
||||||
|
|
|
@ -1670,7 +1670,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
|
||||||
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) {
|
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) {
|
||||||
const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferrable);
|
const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable);
|
||||||
if (heldItems.length) {
|
if (heldItems.length) {
|
||||||
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
||||||
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
|
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
|
||||||
|
@ -1763,7 +1763,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
|
||||||
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
|
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
|
||||||
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable);
|
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable);
|
||||||
if (heldItems.length) {
|
if (heldItems.length) {
|
||||||
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
||||||
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
|
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
|
||||||
|
@ -2625,7 +2625,11 @@ export class PreStatStageChangeAbAttr extends AbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protect one or all {@linkcode BattleStat} from reductions caused by other Pokémon's moves and Abilities
|
||||||
|
*/
|
||||||
export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
|
export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
|
||||||
|
/** {@linkcode BattleStat} to protect or `undefined` if **all** {@linkcode BattleStat} are protected */
|
||||||
private protectedStat?: BattleStat;
|
private protectedStat?: BattleStat;
|
||||||
|
|
||||||
constructor(protectedStat?: BattleStat) {
|
constructor(protectedStat?: BattleStat) {
|
||||||
|
@ -2634,7 +2638,17 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr {
|
||||||
this.protectedStat = protectedStat;
|
this.protectedStat = protectedStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean {
|
/**
|
||||||
|
* Apply the {@linkcode ProtectedStatAbAttr} to an interaction
|
||||||
|
* @param _pokemon
|
||||||
|
* @param _passive
|
||||||
|
* @param simulated
|
||||||
|
* @param stat the {@linkcode BattleStat} being affected
|
||||||
|
* @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true if the stat is protected
|
||||||
|
* @param _args
|
||||||
|
* @returns true if the stat is protected, false otherwise
|
||||||
|
*/
|
||||||
|
applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean {
|
||||||
if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) {
|
if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) {
|
||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -3757,7 +3771,7 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr {
|
||||||
this.multiplier = multiplier;
|
this.multiplier = multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
(args[0] as Utils.IntegerHolder).value *= this.multiplier;
|
(args[0] as Utils.IntegerHolder).value *= this.multiplier;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -488,14 +488,14 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
||||||
} else {
|
} else {
|
||||||
moveAnims.set(move, null);
|
moveAnims.set(move, null);
|
||||||
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
|
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
|
||||||
const moveName = Moves[move].toLowerCase().replace(/\_/g, "-");
|
|
||||||
const fetchAnimAndResolve = (move: Moves) => {
|
const fetchAnimAndResolve = (move: Moves) => {
|
||||||
scene.cachedFetch(`./battle-anims/${moveName}.json`)
|
scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const contentType = response.headers.get("content-type");
|
const contentType = response.headers.get("content-type");
|
||||||
if (!response.ok || contentType?.indexOf("application/json") === -1) {
|
if (!response.ok || contentType?.indexOf("application/json") === -1) {
|
||||||
console.error(`Could not load animation file for move '${moveName}'`, response.status, response.statusText);
|
useDefaultAnim(move, defaultMoveAnim);
|
||||||
populateMoveAnim(move, moveAnims.get(defaultMoveAnim));
|
logMissingMoveAnim(move, response.status, response.statusText);
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
|
@ -515,6 +515,11 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
useDefaultAnim(move, defaultMoveAnim);
|
||||||
|
logMissingMoveAnim(move, error);
|
||||||
|
return resolve();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
fetchAnimAndResolve(move);
|
fetchAnimAndResolve(move);
|
||||||
|
@ -522,6 +527,29 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the default animation for the given move.
|
||||||
|
*
|
||||||
|
* @param move the move to populate an animation for
|
||||||
|
* @param defaultMoveAnim the move to use as the default animation
|
||||||
|
*/
|
||||||
|
function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) {
|
||||||
|
populateMoveAnim(move, moveAnims.get(defaultMoveAnim));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for printing a warning to the console when a move animation is missing.
|
||||||
|
*
|
||||||
|
* @param move the move to populate an animation for
|
||||||
|
* @param optionalParams parameters to add to the error logging
|
||||||
|
*
|
||||||
|
* @remarks use {@linkcode useDefaultAnim} to use a default animation
|
||||||
|
*/
|
||||||
|
function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
|
||||||
|
const moveName = Utils.animationFileName(move);
|
||||||
|
console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches animation configs to be used in a Mystery Encounter
|
* Fetches animation configs to be used in a Mystery Encounter
|
||||||
* @param scene
|
* @param scene
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
|
||||||
import { VariantTier } from "../enums/variant-tiers";
|
import { VariantTier } from "../enums/variant-tier";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { pokemonPrevolutions } from "./pokemon-evolutions";
|
import { pokemonPrevolutions } from "./pokemon-evolutions";
|
||||||
|
@ -178,7 +178,7 @@ export class Egg {
|
||||||
// be done because species with no variants get filtered at rollSpecies but if the
|
// be done because species with no variants get filtered at rollSpecies but if the
|
||||||
// species is set via options or the legendary gacha pokemon gets choosen the check never happens
|
// species is set via options or the legendary gacha pokemon gets choosen the check never happens
|
||||||
if (this._species && !getPokemonSpecies(this._species).hasVariants()) {
|
if (this._species && !getPokemonSpecies(this._species).hasVariants()) {
|
||||||
this._variantTier = VariantTier.COMMON;
|
this._variantTier = VariantTier.STANDARD;
|
||||||
}
|
}
|
||||||
// Needs this._tier so it needs to be generated afer the tier override if bought from same species
|
// Needs this._tier so it needs to be generated afer the tier override if bought from same species
|
||||||
this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
|
this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
|
||||||
|
@ -494,12 +494,12 @@ export class Egg {
|
||||||
// place but I don't want to touch the pokemon class.
|
// place but I don't want to touch the pokemon class.
|
||||||
private rollVariant(): VariantTier {
|
private rollVariant(): VariantTier {
|
||||||
if (!this.isShiny) {
|
if (!this.isShiny) {
|
||||||
return VariantTier.COMMON;
|
return VariantTier.STANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rand = Utils.randSeedInt(10);
|
const rand = Utils.randSeedInt(10);
|
||||||
if (rand >= 4) {
|
if (rand >= 4) {
|
||||||
return VariantTier.COMMON; // 6/10
|
return VariantTier.STANDARD; // 6/10
|
||||||
} else if (rand >= 1) {
|
} else if (rand >= 1) {
|
||||||
return VariantTier.RARE; // 3/10
|
return VariantTier.RARE; // 3/10
|
||||||
} else {
|
} else {
|
||||||
|
|
121
src/data/move.ts
121
src/data/move.ts
|
@ -1,16 +1,15 @@
|
||||||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
import { ChargeAnim, initMoveAnim, loadMoveAnimAssets, MoveChargeAnim } from "./battle-anims";
|
||||||
import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, SubstituteTag, TypeBoostTag } from "./battler-tags";
|
import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, SubstituteTag, TrappedTag, TypeBoostTag } from "./battler-tags";
|
||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
||||||
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect";
|
import { getNonVolatileStatusEffects, getStatusEffectHealText, isNonVolatileStatusEffect, StatusEffect } from "./status-effect";
|
||||||
import { getTypeDamageMultiplier, Type } from "./type";
|
import { getTypeDamageMultiplier, Type } from "./type";
|
||||||
import { Constructor } from "#app/utils";
|
import { Constructor, NumberHolder } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { WeatherType } from "./weather";
|
import { WeatherType } from "./weather";
|
||||||
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
||||||
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAbAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability";
|
import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability";
|
||||||
import { allAbilities } from "./ability";
|
import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier";
|
||||||
import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier";
|
|
||||||
import { BattlerIndex, BattleType } from "../battle";
|
import { BattlerIndex, BattleType } from "../battle";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
||||||
|
@ -25,7 +24,7 @@ import { Biome } from "#enums/biome";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { MoveUsedEvent } from "#app/events/battle-scene";
|
import { MoveUsedEvent } from "#app/events/battle-scene";
|
||||||
import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat";
|
import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat";
|
||||||
import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase";
|
import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase";
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
|
@ -36,7 +35,6 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
||||||
import { NumberHolder } from "#app/utils";
|
|
||||||
import { GameMode } from "#app/game-mode";
|
import { GameMode } from "#app/game-mode";
|
||||||
import { applyChallenges, ChallengeType } from "./challenge";
|
import { applyChallenges, ChallengeType } from "./challenge";
|
||||||
|
|
||||||
|
@ -2136,7 +2134,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
|
||||||
if (rand >= this.chance) {
|
if (rand >= this.chance) {
|
||||||
return resolve(false);
|
return resolve(false);
|
||||||
}
|
}
|
||||||
const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable);
|
const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable);
|
||||||
if (heldItems.length) {
|
if (heldItems.length) {
|
||||||
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
|
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
|
||||||
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
|
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
|
||||||
|
@ -2213,7 +2211,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Considers entire transferrable item pool by default (Knock Off). Otherwise berries only if specified (Incinerate).
|
// Considers entire transferrable item pool by default (Knock Off). Otherwise berries only if specified (Incinerate).
|
||||||
let heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable);
|
let heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable);
|
||||||
|
|
||||||
if (this.berriesOnly) {
|
if (this.berriesOnly) {
|
||||||
heldItems = heldItems.filter(m => m instanceof BerryModifier && m.pokemonId === target.id, target.isPlayer());
|
heldItems = heldItems.filter(m => m instanceof BerryModifier && m.pokemonId === target.id, target.isPlayer());
|
||||||
|
@ -2417,6 +2415,16 @@ export class BypassSleepAttr extends MoveAttr {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns arbitrarily high score when Pokemon is asleep, otherwise shouldn't be used
|
||||||
|
* @param user
|
||||||
|
* @param target
|
||||||
|
* @param move
|
||||||
|
*/
|
||||||
|
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||||
|
return user.status && user.status.effect === StatusEffect.SLEEP ? 200 : -10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3839,7 +3847,7 @@ export class StormAccuracyAttr extends VariableAccuracyAttr {
|
||||||
* @extends VariableAccuracyAttr
|
* @extends VariableAccuracyAttr
|
||||||
* @see {@linkcode apply}
|
* @see {@linkcode apply}
|
||||||
*/
|
*/
|
||||||
export class MinimizeAccuracyAttr extends VariableAccuracyAttr {
|
export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr {
|
||||||
/**
|
/**
|
||||||
* @see {@linkcode apply}
|
* @see {@linkcode apply}
|
||||||
* @param user N/A
|
* @param user N/A
|
||||||
|
@ -3974,18 +3982,17 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr {
|
||||||
export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const category = (args[0] as Utils.NumberHolder);
|
const category = (args[0] as Utils.NumberHolder);
|
||||||
const atkRatio = user.getEffectiveStat(Stat.ATK, target, move) / target.getEffectiveStat(Stat.DEF, user, move);
|
|
||||||
const specialRatio = user.getEffectiveStat(Stat.SPATK, target, move) / target.getEffectiveStat(Stat.SPDEF, user, move);
|
|
||||||
|
|
||||||
// Shell Side Arm is much more complicated than it looks, this is a partial implementation to try to achieve something similar to the games
|
const predictedPhysDmg = target.getBaseDamage(user, move, MoveCategory.PHYSICAL, true, true);
|
||||||
if (atkRatio > specialRatio) {
|
const predictedSpecDmg = target.getBaseDamage(user, move, MoveCategory.SPECIAL, true, true);
|
||||||
|
|
||||||
|
if (predictedPhysDmg > predictedSpecDmg) {
|
||||||
category.value = MoveCategory.PHYSICAL;
|
category.value = MoveCategory.PHYSICAL;
|
||||||
return true;
|
return true;
|
||||||
} else if (atkRatio === specialRatio && user.randSeedInt(2) === 0) {
|
} else if (predictedPhysDmg === predictedSpecDmg && user.randSeedInt(2) === 0) {
|
||||||
category.value = MoveCategory.PHYSICAL;
|
category.value = MoveCategory.PHYSICAL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4852,7 +4859,9 @@ export class RemoveAllSubstitutesAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute used when a move hits a {@linkcode BattlerTagType} for double damage
|
* Attribute used when a move can deal damage to {@linkcode BattlerTagType}
|
||||||
|
* Moves that always hit but do not deal double damage: Thunder, Fissure, Sky Uppercut,
|
||||||
|
* Smack Down, Hurricane, Thousand Arrows
|
||||||
* @extends MoveAttr
|
* @extends MoveAttr
|
||||||
*/
|
*/
|
||||||
export class HitsTagAttr extends MoveAttr {
|
export class HitsTagAttr extends MoveAttr {
|
||||||
|
@ -4861,7 +4870,7 @@ export class HitsTagAttr extends MoveAttr {
|
||||||
/** Should this move deal double damage against {@linkcode HitsTagAttr.tagType}? */
|
/** Should this move deal double damage against {@linkcode HitsTagAttr.tagType}? */
|
||||||
public doubleDamage: boolean;
|
public doubleDamage: boolean;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, doubleDamage?: boolean) {
|
constructor(tagType: BattlerTagType, doubleDamage: boolean = false) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.tagType = tagType;
|
this.tagType = tagType;
|
||||||
|
@ -4873,6 +4882,17 @@ export class HitsTagAttr extends MoveAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for moves that will always hit for a given tag but also doubles damage.
|
||||||
|
* Moves include: Gust, Stomp, Body Slam, Surf, Earthquake, Magnitude, Twister,
|
||||||
|
* Whirlpool, Dragon Rush, Heat Crash, Steam Roller, Flying Press
|
||||||
|
*/
|
||||||
|
export class HitsTagForDoubleDamageAttr extends HitsTagAttr {
|
||||||
|
constructor(tagType: BattlerTagType) {
|
||||||
|
super(tagType, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class AddArenaTagAttr extends MoveEffectAttr {
|
export class AddArenaTagAttr extends MoveEffectAttr {
|
||||||
public tagType: ArenaTagType;
|
public tagType: ArenaTagType;
|
||||||
public turnCount: integer;
|
public turnCount: integer;
|
||||||
|
@ -6393,7 +6413,7 @@ export class AttackedByItemAttr extends MoveAttr {
|
||||||
*/
|
*/
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user: Pokemon, target: Pokemon, move: Move) => {
|
return (user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
const heldItems = target.getHeldItems().filter(i => i.isTransferrable);
|
const heldItems = target.getHeldItems().filter(i => i.isTransferable);
|
||||||
if (heldItems.length === 0) {
|
if (heldItems.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -6752,12 +6772,11 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.CUT, Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1)
|
new AttackMove(Moves.CUT, Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1)
|
||||||
.slicingMove(),
|
.slicingMove(),
|
||||||
new AttackMove(Moves.GUST, Type.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, 0, 1)
|
new AttackMove(Moves.GUST, Type.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, 0, 1)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING)
|
||||||
.windMove(),
|
.windMove(),
|
||||||
new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1),
|
new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1),
|
||||||
new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1)
|
new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1)
|
||||||
.attr(ForceSwitchOutAttr)
|
.attr(ForceSwitchOutAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false)
|
|
||||||
.ignoresSubstitute()
|
.ignoresSubstitute()
|
||||||
.hidesTarget()
|
.hidesTarget()
|
||||||
.windMove(),
|
.windMove(),
|
||||||
|
@ -6770,8 +6789,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1),
|
new AttackMove(Moves.SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1),
|
||||||
new AttackMove(Moves.VINE_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1),
|
new AttackMove(Moves.VINE_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1),
|
||||||
new AttackMove(Moves.STOMP, Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 1)
|
new AttackMove(Moves.STOMP, Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 1)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED)
|
||||||
.attr(FlinchAttr),
|
.attr(FlinchAttr),
|
||||||
new AttackMove(Moves.DOUBLE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 30, 100, 30, -1, 0, 1)
|
new AttackMove(Moves.DOUBLE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 30, 100, 30, -1, 0, 1)
|
||||||
.attr(MultiHitAttr, MultiHitType._2),
|
.attr(MultiHitAttr, MultiHitType._2),
|
||||||
|
@ -6795,8 +6814,8 @@ export function initMoves() {
|
||||||
.attr(OneHitKOAccuracyAttr),
|
.attr(OneHitKOAccuracyAttr),
|
||||||
new AttackMove(Moves.TACKLE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1),
|
new AttackMove(Moves.TACKLE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1),
|
||||||
new AttackMove(Moves.BODY_SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 85, 100, 15, 30, 0, 1)
|
new AttackMove(Moves.BODY_SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 85, 100, 15, 30, 0, 1)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED)
|
||||||
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
|
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
|
||||||
new AttackMove(Moves.WRAP, Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1)
|
new AttackMove(Moves.WRAP, Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1)
|
||||||
.attr(TrapAttr, BattlerTagType.WRAP),
|
.attr(TrapAttr, BattlerTagType.WRAP),
|
||||||
|
@ -6864,7 +6883,7 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.HYDRO_PUMP, Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, -1, 0, 1),
|
new AttackMove(Moves.HYDRO_PUMP, Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, -1, 0, 1),
|
||||||
new AttackMove(Moves.SURF, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 1)
|
new AttackMove(Moves.SURF, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 1)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS)
|
.target(MoveTarget.ALL_NEAR_OTHERS)
|
||||||
.attr(HitsTagAttr, BattlerTagType.UNDERWATER, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER)
|
||||||
.attr(GulpMissileTagAttr),
|
.attr(GulpMissileTagAttr),
|
||||||
new AttackMove(Moves.ICE_BEAM, Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1)
|
new AttackMove(Moves.ICE_BEAM, Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1)
|
||||||
.attr(StatusEffectAttr, StatusEffect.FREEZE),
|
.attr(StatusEffectAttr, StatusEffect.FREEZE),
|
||||||
|
@ -6947,18 +6966,18 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.THUNDER, Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 1)
|
new AttackMove(Moves.THUNDER, Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 1)
|
||||||
.attr(StatusEffectAttr, StatusEffect.PARALYSIS)
|
.attr(StatusEffectAttr, StatusEffect.PARALYSIS)
|
||||||
.attr(ThunderAccuracyAttr)
|
.attr(ThunderAccuracyAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false),
|
.attr(HitsTagAttr, BattlerTagType.FLYING),
|
||||||
new AttackMove(Moves.ROCK_THROW, Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, 0, 1)
|
new AttackMove(Moves.ROCK_THROW, Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, 0, 1)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
new AttackMove(Moves.EARTHQUAKE, Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 1)
|
new AttackMove(Moves.EARTHQUAKE, Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 1)
|
||||||
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1)
|
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1)
|
||||||
.makesContact(false)
|
.makesContact(false)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.FISSURE, Type.GROUND, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1)
|
new AttackMove(Moves.FISSURE, Type.GROUND, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1)
|
||||||
.attr(OneHitKOAttr)
|
.attr(OneHitKOAttr)
|
||||||
.attr(OneHitKOAccuracyAttr)
|
.attr(OneHitKOAccuracyAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, false)
|
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
new AttackMove(Moves.DIG, Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 1)
|
new AttackMove(Moves.DIG, Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 1)
|
||||||
.attr(ChargeAttr, ChargeAnim.DIG_CHARGING, i18next.t("moveTriggers:dugAHole", {pokemonName: "{USER}"}), BattlerTagType.UNDERGROUND)
|
.attr(ChargeAttr, ChargeAnim.DIG_CHARGING, i18next.t("moveTriggers:dugAHole", {pokemonName: "{USER}"}), BattlerTagType.UNDERGROUND)
|
||||||
|
@ -7347,7 +7366,7 @@ export function initMoves() {
|
||||||
.attr(PreMoveMessageAttr, magnitudeMessageFunc)
|
.attr(PreMoveMessageAttr, magnitudeMessageFunc)
|
||||||
.attr(MagnitudePowerAttr)
|
.attr(MagnitudePowerAttr)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1)
|
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1)
|
||||||
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND)
|
||||||
.makesContact(false)
|
.makesContact(false)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.DYNAMIC_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, 100, 0, 2)
|
new AttackMove(Moves.DYNAMIC_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, 100, 0, 2)
|
||||||
|
@ -7403,7 +7422,7 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.CROSS_CHOP, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, 0, 2)
|
new AttackMove(Moves.CROSS_CHOP, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, 0, 2)
|
||||||
.attr(HighCritAttr),
|
.attr(HighCritAttr),
|
||||||
new AttackMove(Moves.TWISTER, Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, 20, 0, 2)
|
new AttackMove(Moves.TWISTER, Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, 20, 0, 2)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING)
|
||||||
.attr(FlinchAttr)
|
.attr(FlinchAttr)
|
||||||
.windMove()
|
.windMove()
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
|
@ -7435,7 +7454,7 @@ export function initMoves() {
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], -1),
|
.attr(StatStageChangeAttr, [ Stat.DEF ], -1),
|
||||||
new AttackMove(Moves.WHIRLPOOL, Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2)
|
new AttackMove(Moves.WHIRLPOOL, Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2)
|
||||||
.attr(TrapAttr, BattlerTagType.WHIRLPOOL)
|
.attr(TrapAttr, BattlerTagType.WHIRLPOOL)
|
||||||
.attr(HitsTagAttr, BattlerTagType.UNDERWATER, true),
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER),
|
||||||
new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2)
|
new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2)
|
||||||
.attr(MultiHitAttr, MultiHitType.BEAT_UP)
|
.attr(MultiHitAttr, MultiHitType.BEAT_UP)
|
||||||
.attr(BeatUpAttr)
|
.attr(BeatUpAttr)
|
||||||
|
@ -7533,7 +7552,7 @@ export function initMoves() {
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
|
||||||
.condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)),
|
.condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)),
|
||||||
new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
|
new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1)
|
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferable).length > 0 ? 1.5 : 1)
|
||||||
.attr(RemoveHeldItemAttr, false),
|
.attr(RemoveHeldItemAttr, false),
|
||||||
new AttackMove(Moves.ENDEAVOR, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 3)
|
new AttackMove(Moves.ENDEAVOR, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 3)
|
||||||
.attr(MatchHpAttr)
|
.attr(MatchHpAttr)
|
||||||
|
@ -7889,8 +7908,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.DRAGON_PULSE, Type.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4)
|
new AttackMove(Moves.DRAGON_PULSE, Type.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4)
|
||||||
.pulseMove(),
|
.pulseMove(),
|
||||||
new AttackMove(Moves.DRAGON_RUSH, Type.DRAGON, MoveCategory.PHYSICAL, 100, 75, 10, 20, 0, 4)
|
new AttackMove(Moves.DRAGON_RUSH, Type.DRAGON, MoveCategory.PHYSICAL, 100, 75, 10, 20, 0, 4)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED)
|
||||||
.attr(FlinchAttr),
|
.attr(FlinchAttr),
|
||||||
new AttackMove(Moves.POWER_GEM, Type.ROCK, MoveCategory.SPECIAL, 80, 100, 20, -1, 0, 4),
|
new AttackMove(Moves.POWER_GEM, Type.ROCK, MoveCategory.SPECIAL, 80, 100, 20, -1, 0, 4),
|
||||||
new AttackMove(Moves.DRAIN_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 4)
|
new AttackMove(Moves.DRAIN_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 4)
|
||||||
|
@ -8087,7 +8106,7 @@ export function initMoves() {
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
||||||
.attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN])
|
.attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN])
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false)
|
.attr(HitsTagAttr, BattlerTagType.FLYING)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5)
|
new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5)
|
||||||
.attr(CritOnlyAttr),
|
.attr(CritOnlyAttr),
|
||||||
|
@ -8100,9 +8119,9 @@ export function initMoves() {
|
||||||
.attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true)
|
.attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true)
|
||||||
.danceMove(),
|
.danceMove(),
|
||||||
new AttackMove(Moves.HEAVY_SLAM, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
|
new AttackMove(Moves.HEAVY_SLAM, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(CompareWeightPowerAttr)
|
.attr(CompareWeightPowerAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true),
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED),
|
||||||
new AttackMove(Moves.SYNCHRONOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 5)
|
new AttackMove(Moves.SYNCHRONOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 5)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS)
|
.target(MoveTarget.ALL_NEAR_OTHERS)
|
||||||
.condition(unknownTypeCondition)
|
.condition(unknownTypeCondition)
|
||||||
|
@ -8185,7 +8204,7 @@ export function initMoves() {
|
||||||
new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5)
|
new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5)
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
|
new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferrable).reduce((v, m) => v + m.stackCount, 0))),
|
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))),
|
||||||
new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5)
|
new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5)
|
||||||
.ignoresSubstitute()
|
.ignoresSubstitute()
|
||||||
.attr(CopyTypeAttr),
|
.attr(CopyTypeAttr),
|
||||||
|
@ -8253,12 +8272,14 @@ export function initMoves() {
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], -1)
|
.attr(StatStageChangeAttr, [ Stat.DEF ], -1)
|
||||||
.slicingMove(),
|
.slicingMove(),
|
||||||
new AttackMove(Moves.HEAT_CRASH, Type.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
|
new AttackMove(Moves.HEAT_CRASH, Type.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(CompareWeightPowerAttr)
|
.attr(CompareWeightPowerAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true),
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED),
|
||||||
new AttackMove(Moves.LEAF_TORNADO, Type.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5)
|
new AttackMove(Moves.LEAF_TORNADO, Type.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5)
|
||||||
.attr(StatStageChangeAttr, [ Stat.ACC ], -1),
|
.attr(StatStageChangeAttr, [ Stat.ACC ], -1),
|
||||||
new AttackMove(Moves.STEAMROLLER, Type.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5)
|
new AttackMove(Moves.STEAMROLLER, Type.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5)
|
||||||
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED)
|
||||||
.attr(FlinchAttr),
|
.attr(FlinchAttr),
|
||||||
new SelfStatusMove(Moves.COTTON_GUARD, Type.GRASS, -1, 10, -1, 0, 5)
|
new SelfStatusMove(Moves.COTTON_GUARD, Type.GRASS, -1, 10, -1, 0, 5)
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 3, true),
|
.attr(StatStageChangeAttr, [ Stat.DEF ], 3, true),
|
||||||
|
@ -8271,7 +8292,7 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.HURRICANE, Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 5)
|
new AttackMove(Moves.HURRICANE, Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 5)
|
||||||
.attr(ThunderAccuracyAttr)
|
.attr(ThunderAccuracyAttr)
|
||||||
.attr(ConfuseAttr)
|
.attr(ConfuseAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false)
|
.attr(HitsTagAttr, BattlerTagType.FLYING)
|
||||||
.windMove(),
|
.windMove(),
|
||||||
new AttackMove(Moves.HEAD_CHARGE, Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 5)
|
new AttackMove(Moves.HEAD_CHARGE, Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 5)
|
||||||
.attr(RecoilAttr)
|
.attr(RecoilAttr)
|
||||||
|
@ -8325,9 +8346,9 @@ export function initMoves() {
|
||||||
.attr(LastMoveDoublePowerAttr, Moves.FUSION_FLARE)
|
.attr(LastMoveDoublePowerAttr, Moves.FUSION_FLARE)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
new AttackMove(Moves.FLYING_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, 0, 6)
|
new AttackMove(Moves.FLYING_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, 0, 6)
|
||||||
.attr(MinimizeAccuracyAttr)
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
.attr(FlyingTypeMultiplierAttr)
|
.attr(FlyingTypeMultiplierAttr)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
.attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED)
|
||||||
.condition(failOnGravityCondition),
|
.condition(failOnGravityCondition),
|
||||||
new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6)
|
new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
|
@ -8498,8 +8519,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.THOUSAND_ARROWS, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6)
|
new AttackMove(Moves.THOUSAND_ARROWS, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6)
|
||||||
.attr(NeutralDamageAgainstFlyingTypeMultiplierAttr)
|
.attr(NeutralDamageAgainstFlyingTypeMultiplierAttr)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
|
||||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false)
|
.attr(HitsTagAttr, BattlerTagType.FLYING)
|
||||||
.attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN, false)
|
.attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
||||||
.attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN])
|
.attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN])
|
||||||
.makesContact(false)
|
.makesContact(false)
|
||||||
|
@ -8756,6 +8777,8 @@ export function initMoves() {
|
||||||
.partial()
|
.partial()
|
||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
|
new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
|
||||||
|
.attr(AlwaysHitMinimizeAttr)
|
||||||
|
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
||||||
.partial()
|
.partial()
|
||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7)
|
new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7)
|
||||||
|
@ -9103,7 +9126,7 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.SHELL_SIDE_ARM, Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8)
|
new AttackMove(Moves.SHELL_SIDE_ARM, Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8)
|
||||||
.attr(ShellSideArmCategoryAttr)
|
.attr(ShellSideArmCategoryAttr)
|
||||||
.attr(StatusEffectAttr, StatusEffect.POISON)
|
.attr(StatusEffectAttr, StatusEffect.POISON)
|
||||||
.partial(),
|
.partial(), // Physical version of the move does not make contact
|
||||||
new AttackMove(Moves.MISTY_EXPLOSION, Type.FAIRY, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8)
|
new AttackMove(Moves.MISTY_EXPLOSION, Type.FAIRY, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8)
|
||||||
.attr(SacrificialAttr)
|
.attr(SacrificialAttr)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS)
|
.target(MoveTarget.ALL_NEAR_OTHERS)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattl
|
||||||
import { trainerConfigs, } from "#app/data/trainer-config";
|
import { trainerConfigs, } from "#app/data/trainer-config";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -99,7 +99,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||||
const trainerConfig = trainerConfigs[trainerType].clone();
|
const trainerConfig = trainerConfigs[trainerType].clone();
|
||||||
const trainerSpriteKey = trainerConfig.getSpriteKey();
|
const trainerSpriteKey = trainerConfig.getSpriteKey();
|
||||||
encounter.enemyPartyConfigs.push({
|
encounter.enemyPartyConfigs.push({
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveModifier: 1,
|
||||||
trainerConfig: trainerConfig
|
trainerConfig: trainerConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,7 +152,6 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||||
};
|
};
|
||||||
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
|
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]);
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]);
|
||||||
|
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -180,7 +179,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||||
)
|
)
|
||||||
.withOutroDialogue([
|
.withOutroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}.outro`,
|
text: `${namespace}.outro`
|
||||||
},
|
}
|
||||||
])
|
])
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { PersistentModifierRequirement } from "../mystery-encounter-requirements";
|
import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -208,7 +208,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveModifier: 1,
|
||||||
pokemonConfigs: [
|
pokemonConfigs: [
|
||||||
{
|
{
|
||||||
species: getPokemonSpecies(Species.GREEDENT),
|
species: getPokemonSpecies(Species.GREEDENT),
|
||||||
|
@ -291,7 +291,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
const berryMap = encounter.misc.berryItemsMap;
|
const berryMap = encounter.misc.berryItemsMap;
|
||||||
|
|
||||||
// Returns 2/5 of the berries stolen from each Pokemon
|
// Returns 2/5 of the berries stolen to each Pokemon
|
||||||
const party = scene.getParty();
|
const party = scene.getParty();
|
||||||
party.forEach(pokemon => {
|
party.forEach(pokemon => {
|
||||||
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
||||||
|
@ -310,6 +310,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "../mystery-encounter-requirements";
|
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
|
|
@ -17,13 +17,13 @@ import { randSeedInt } from "#app/utils";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { applyModifierTypeToPlayerPokemon, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
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 PokemonData from "#app/system/pokemon-data";
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
|
@ -56,12 +56,11 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
|
||||||
pokemonConfigs: [{
|
pokemonConfigs: [{
|
||||||
level: level,
|
level: level,
|
||||||
species: bossSpecies,
|
species: bossSpecies,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import {
|
import {
|
||||||
|
getRandomPartyMemberFunc,
|
||||||
trainerConfigs,
|
trainerConfigs,
|
||||||
TrainerPartyCompoundTemplate,
|
TrainerPartyCompoundTemplate,
|
||||||
TrainerPartyTemplate,
|
TrainerPartyTemplate,
|
||||||
|
@ -17,14 +18,12 @@ import {
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
|
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|
||||||
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
@ -192,6 +191,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||||
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1),
|
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1),
|
||||||
new TypeRequirement(Type.BUG, false, 1)
|
new TypeRequirement(Type.BUG, false, 1)
|
||||||
))
|
))
|
||||||
|
.withMaxAllowedEncounters(1)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
.withAutoHideIntroVisuals(false)
|
.withAutoHideIntroVisuals(false)
|
||||||
|
@ -286,7 +286,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Player gets different rewards depending on the number of bug types they have
|
// Player gets different rewards depending on the number of bug types they have
|
||||||
const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length;
|
const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length;
|
||||||
encounter.setDialogueToken("numBugTypes", numBugTypes.toString());
|
const numBugTypesText = i18next.t(`${namespace}.numBugTypes`, { count: numBugTypes });
|
||||||
|
encounter.setDialogueToken("numBugTypes", numBugTypesText);
|
||||||
|
|
||||||
if (numBugTypes < 2) {
|
if (numBugTypes < 2) {
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], fillRemaining: false });
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], fillRemaining: false });
|
||||||
|
@ -582,16 +583,6 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
|
||||||
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
|
||||||
let species = Utils.randSeedItem(speciesPool);
|
|
||||||
if (!ignoreEvolution) {
|
|
||||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength);
|
|
||||||
}
|
|
||||||
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, undefined, postProcess);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
||||||
return new Promise<void>(async resolve => {
|
return new Promise<void>(async resolve => {
|
||||||
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
|
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
@ -246,12 +246,12 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||||
const party = scene.getParty();
|
const party = scene.getParty();
|
||||||
let mostHeldItemsPokemon = party[0];
|
let mostHeldItemsPokemon = party[0];
|
||||||
let count = mostHeldItemsPokemon.getHeldItems()
|
let count = mostHeldItemsPokemon.getHeldItems()
|
||||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||||
.reduce((v, m) => v + m.stackCount, 0);
|
.reduce((v, m) => v + m.stackCount, 0);
|
||||||
|
|
||||||
party.forEach(pokemon => {
|
party.forEach(pokemon => {
|
||||||
const nextCount = pokemon.getHeldItems()
|
const nextCount = pokemon.getHeldItems()
|
||||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||||
.reduce((v, m) => v + m.stackCount, 0);
|
.reduce((v, m) => v + m.stackCount, 0);
|
||||||
if (nextCount > count) {
|
if (nextCount > count) {
|
||||||
mostHeldItemsPokemon = pokemon;
|
mostHeldItemsPokemon = pokemon;
|
||||||
|
@ -276,7 +276,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||||
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
|
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
|
||||||
let numUltra = 0;
|
let numUltra = 0;
|
||||||
let numRogue = 0;
|
let numRogue = 0;
|
||||||
items.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
items.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||||
.forEach(m => {
|
.forEach(m => {
|
||||||
const type = m.type.withTierFromPool();
|
const type = m.type.withTierFromPool();
|
||||||
const tier = type.tier ?? ModifierTier.ULTRA;
|
const tier = type.tier ?? ModifierTier.ULTRA;
|
||||||
|
|
|
@ -3,8 +3,8 @@ import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/po
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
@ -19,7 +19,7 @@ import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-
|
||||||
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { catchPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
|
@ -107,7 +107,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
const species = getPokemonSpecies(Species.ORICORIO);
|
const species = getPokemonSpecies(Species.ORICORIO);
|
||||||
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false);
|
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false);
|
||||||
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
|
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
|
||||||
if (enemyPokemon.moveset.length < 4) {
|
if (enemyPokemon.moveset.length < 4) {
|
||||||
|
@ -133,7 +133,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
}
|
}
|
||||||
|
|
||||||
const oricorioData = new PokemonData(enemyPokemon);
|
const oricorioData = new PokemonData(enemyPokemon);
|
||||||
const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData);
|
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, oricorioData);
|
||||||
|
|
||||||
// Adds a real Pokemon sprite to the field (required for the animation)
|
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||||
|
@ -146,7 +146,6 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
encounter.loadAssets.push(oricorio.loadAssets());
|
encounter.loadAssets.push(oricorio.loadAssets());
|
||||||
|
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
|
||||||
pokemonConfigs: [{
|
pokemonConfigs: [{
|
||||||
species: species,
|
species: species,
|
||||||
dataSource: oricorioData,
|
dataSource: oricorioData,
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
|
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
|
||||||
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
@ -18,7 +18,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
/** i18n namespace for encounter */
|
/** i18n namespace for encounter */
|
||||||
const namespace = "mysteryEncounter:darkDeal";
|
const namespace = "mysteryEncounter:darkDeal";
|
||||||
|
|
||||||
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */
|
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */
|
||||||
const excludedBosses = [
|
const excludedBosses = [
|
||||||
Species.NECROZMA,
|
Species.NECROZMA,
|
||||||
Species.COSMOG,
|
Species.COSMOG,
|
||||||
|
@ -63,11 +63,24 @@ const excludedBosses = [
|
||||||
Species.CELEBI,
|
Species.CELEBI,
|
||||||
Species.DEOXYS,
|
Species.DEOXYS,
|
||||||
Species.JIRACHI,
|
Species.JIRACHI,
|
||||||
|
Species.DARKRAI,
|
||||||
Species.PHIONE,
|
Species.PHIONE,
|
||||||
Species.MANAPHY,
|
Species.MANAPHY,
|
||||||
Species.ARCEUS,
|
Species.ARCEUS,
|
||||||
|
Species.SHAYMIN,
|
||||||
Species.VICTINI,
|
Species.VICTINI,
|
||||||
|
Species.MELOETTA,
|
||||||
|
Species.KELDEO,
|
||||||
|
Species.GENESECT,
|
||||||
|
Species.DIANCIE,
|
||||||
|
Species.HOOPA,
|
||||||
|
Species.VOLCANION,
|
||||||
|
Species.MAGEARNA,
|
||||||
|
Species.MARSHADOW,
|
||||||
|
Species.ZERAORA,
|
||||||
|
Species.ZARUDE,
|
||||||
Species.MELTAN,
|
Species.MELTAN,
|
||||||
|
Species.MELMETAL,
|
||||||
Species.PECHARUNT,
|
Species.PECHARUNT,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -151,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter =
|
||||||
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
|
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
|
||||||
const roll = randSeedInt(100);
|
const roll = randSeedInt(100);
|
||||||
const starterTier: number | [number, number] =
|
const starterTier: number | [number, number] =
|
||||||
roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
|
roll >= 65 ? 6 : roll >= 15 ? 7 : roll >= 5 ? 8 : [9, 10];
|
||||||
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
||||||
const pokemonConfig: EnemyPokemonConfig = {
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
species: bossSpecies,
|
species: bossSpecies,
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
|
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -33,6 +33,8 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
|
||||||
"PokemonBaseStatTotalModifier"
|
"PokemonBaseStatTotalModifier"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delibird-y encounter.
|
* Delibird-y encounter.
|
||||||
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3804 | GitHub Issue #3804}
|
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3804 | GitHub Issue #3804}
|
||||||
|
@ -42,7 +44,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, 2)) // Must have enough money for it to spawn at the very least
|
.withSceneRequirement(new MoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER)) // Must have enough money for it to spawn at the very least
|
||||||
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
|
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
|
||||||
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
||||||
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
||||||
|
@ -93,12 +95,18 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
.withOnInit((scene: BattleScene) => {
|
.withOnInit((scene: BattleScene) => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
|
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
|
||||||
|
|
||||||
|
scene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3");
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
|
scene.fadeAndSwitchBgm("mystery_encounter_delibirdy");
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 2) // Must have money to spawn
|
.withSceneMoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER) // Must have money to spawn
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.1.label`,
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, {
|
import MysteryEncounter, {
|
||||||
MysteryEncounterBuilder,
|
MysteryEncounterBuilder,
|
||||||
} from "../mystery-encounter";
|
} from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounte
|
||||||
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type";
|
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { TypeRequirement } from "../mystery-encounter-requirements";
|
import { TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
|
|
|
@ -17,12 +17,12 @@ import {
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
@ -54,12 +54,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
|
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
|
||||||
pokemonConfigs: [{
|
pokemonConfigs: [{
|
||||||
level: level,
|
level: level,
|
||||||
species: bossSpecies,
|
species: bossSpecies,
|
||||||
|
@ -69,7 +68,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
|
||||||
// Randomly boost 1 stat 2 stages
|
// Randomly boost 1 stat 2 stages
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(5)], 2));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2));
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
@ -84,12 +84,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOnVisualsStart((scene: BattleScene) => {
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
// Change the bgm
|
scene.fadeAndSwitchBgm("mystery_encounter_fun_and_games");
|
||||||
scene.fadeOutBgm(2000, false);
|
|
||||||
scene.time.delayedCall(2000, () => {
|
|
||||||
scene.playBgm("mystery_encounter_fun_and_games");
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOption(MysteryEncounterOptionBuilder
|
.withOption(MysteryEncounterOptionBuilder
|
||||||
|
@ -175,7 +170,9 @@ async function summonPlayerPokemon(scene: BattleScene) {
|
||||||
const party = scene.getParty();
|
const party = scene.getParty();
|
||||||
const chosenIndex = party.indexOf(playerPokemon);
|
const chosenIndex = party.indexOf(playerPokemon);
|
||||||
if (chosenIndex !== 0) {
|
if (chosenIndex !== 0) {
|
||||||
[party[chosenIndex], party[0]] = [party[chosenIndex], party[chosenIndex]];
|
const leadPokemon = party[0];
|
||||||
|
party[0] = playerPokemon;
|
||||||
|
party[chosenIndex] = leadPokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do trainer summon animation
|
// Do trainer summon animation
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
@ -23,6 +23,7 @@ import { getPokeballAtlasKey, getPokeballTintColor, PokeballType } from "#app/da
|
||||||
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { trainerNamePools } from "#app/data/trainer-names";
|
import { trainerNamePools } from "#app/data/trainer-names";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:globalTradeSystem";
|
const namespace = "mysteryEncounter:globalTradeSystem";
|
||||||
|
@ -118,17 +119,13 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOnVisualsStart((scene: BattleScene) => {
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
// Change the bgm
|
scene.fadeAndSwitchBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey);
|
||||||
scene.fadeOutBgm(1500, false);
|
|
||||||
scene.time.delayedCall(1500, () => {
|
|
||||||
scene.playBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.1.label`,
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
@ -200,6 +197,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
||||||
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000);
|
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000);
|
||||||
scene.playBgm(encounter.misc.bgmKey);
|
scene.playBgm(encounter.misc.bgmKey);
|
||||||
|
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
|
||||||
await hideTradeBackground(scene);
|
await hideTradeBackground(scene);
|
||||||
tradedPokemon.destroy();
|
tradedPokemon.destroy();
|
||||||
|
|
||||||
|
@ -210,6 +208,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.2.label`,
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
@ -218,8 +217,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Randomly generate a Wonder Trade pokemon
|
// Randomly generate a Wonder Trade pokemon
|
||||||
// const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
|
const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
|
||||||
const randomTradeOption = getPokemonSpecies(Species.BURMY);
|
|
||||||
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||||
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
||||||
if (!tradePokemon.shiny) {
|
if (!tradePokemon.shiny) {
|
||||||
|
@ -280,7 +278,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
await showTradeBackground(scene);
|
await showTradeBackground(scene);
|
||||||
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
||||||
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000);
|
await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000);
|
||||||
scene.playBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey);
|
scene.playBgm(encounter.misc.bgmKey);
|
||||||
|
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
|
||||||
await hideTradeBackground(scene);
|
await hideTradeBackground(scene);
|
||||||
tradedPokemon.destroy();
|
tradedPokemon.destroy();
|
||||||
|
|
||||||
|
@ -301,7 +300,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Get Pokemon held items and filter for valid ones
|
// Get Pokemon held items and filter for valid ones
|
||||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
return it.isTransferrable;
|
return it.isTransferable;
|
||||||
});
|
});
|
||||||
|
|
||||||
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
@ -322,7 +321,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon has items to trade
|
// If pokemon has items to trade
|
||||||
const meetsReqs = pokemon.getHeldItems().filter((it) => {
|
const meetsReqs = pokemon.getHeldItems().filter((it) => {
|
||||||
return it.isTransferrable;
|
return it.isTransferable;
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
|
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { Moves } from "#app/enums/moves";
|
||||||
import { Species } from "#app/enums/species";
|
import { Species } from "#app/enums/species";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils";
|
import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils";
|
||||||
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||||
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
|
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
|
||||||
encounter.enemyPartyConfigs.push({
|
encounter.enemyPartyConfigs.push({
|
||||||
trainerConfig: hardConfig,
|
trainerConfig: hardConfig,
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveModifier: 1,
|
||||||
female: female,
|
female: female,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||||
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
||||||
encounter.enemyPartyConfigs.push({
|
encounter.enemyPartyConfigs.push({
|
||||||
trainerConfig: brutalConfig,
|
trainerConfig: brutalConfig,
|
||||||
levelAdditiveMultiplier: 1.5,
|
levelAdditiveModifier: 1.5,
|
||||||
female: female,
|
female: female,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { randSeedInt } from "#app/utils";
|
import { randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
@ -68,7 +68,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 0.5,
|
levelAdditiveModifier: 0.5,
|
||||||
disableSwitch: true,
|
disableSwitch: true,
|
||||||
pokemonConfigs: [
|
pokemonConfigs: [
|
||||||
{
|
{
|
||||||
|
@ -179,6 +179,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||||
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
||||||
await showEncounterText(scene, `${namespace}.option.1.bad`);
|
await showEncounterText(scene, `${namespace}.option.1.bad`);
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
||||||
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
||||||
|
@ -25,7 +25,7 @@ const namespace = "mysteryEncounter:safariZone";
|
||||||
|
|
||||||
const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768];
|
const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768];
|
||||||
|
|
||||||
const SAFARI_MONEY_MULTIPLIER = 2.75;
|
const SAFARI_MONEY_MULTIPLIER = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safari Zone encounter.
|
* Safari Zone encounter.
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
@ -19,6 +19,9 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:shadyVitaminDealer";
|
const namespace = "mysteryEncounter:shadyVitaminDealer";
|
||||||
|
|
||||||
|
const VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER = 1.5;
|
||||||
|
const VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER = 3.5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shady Vitamin Dealer encounter.
|
* Shady Vitamin Dealer encounter.
|
||||||
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3798 | GitHub Issue #3798}
|
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3798 | GitHub Issue #3798}
|
||||||
|
@ -28,7 +31,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER)
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, 1.5)) // Must have the money for at least the cheap deal
|
.withSceneRequirement(new MoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)) // Must have the money for at least the cheap deal
|
||||||
.withPrimaryPokemonHealthRatioRequirement([0.5, 1]) // At least 1 Pokemon must have above half HP
|
.withPrimaryPokemonHealthRatioRequirement([0.5, 1]) // At least 1 Pokemon must have above half HP
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
|
@ -64,7 +67,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 1.5)
|
.withSceneMoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.1.label`,
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
@ -115,7 +118,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
// Damage and status applied after dealer leaves (to make thematic sense)
|
// Damage and status applied after dealer leaves (to make thematic sense)
|
||||||
|
@ -142,7 +145,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 3.5)
|
.withSceneMoneyRequirement(0, VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.2.label`,
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
@ -193,7 +196,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
// Status applied after dealer leaves (to make thematic sense)
|
// Status applied after dealer leaves (to make thematic sense)
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { AiType, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
/** i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:slumberingSnorlax";
|
const namespace = "mysteryEncounter:slumberingSnorlax";
|
||||||
|
@ -38,7 +40,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||||
fileRoot: "pokemon",
|
fileRoot: "pokemon",
|
||||||
hasShadow: true,
|
hasShadow: true,
|
||||||
tint: 0.25,
|
tint: 0.25,
|
||||||
scale: 1.5,
|
scale: 1.25,
|
||||||
repeat: true,
|
repeat: true,
|
||||||
y: 5,
|
y: 5,
|
||||||
},
|
},
|
||||||
|
@ -58,10 +60,22 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||||
species: bossSpecies,
|
species: bossSpecies,
|
||||||
isBoss: true,
|
isBoss: true,
|
||||||
status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves
|
status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves
|
||||||
moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT]
|
moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
|
||||||
|
aiType: AiType.SMART // Required to ensure Snorlax uses Sleep Talk while it is asleep
|
||||||
};
|
};
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 0.5,
|
levelAdditiveModifier: 0.5,
|
||||||
pokemonConfigs: [pokemonConfig],
|
pokemonConfigs: [pokemonConfig],
|
||||||
};
|
};
|
||||||
encounter.enemyPartyConfigs = [config];
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig
|
||||||
import { randSeedInt } from "#app/utils";
|
import { randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MoneyRequirement, WaveModulusRequirement } from "../mystery-encounter-requirements";
|
import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
|
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
@ -20,12 +20,13 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
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 */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:teleportingHijinks";
|
const namespace = "mysteryEncounter:teleportingHijinks";
|
||||||
|
|
||||||
const MONEY_COST_MULTIPLIER = 2.5;
|
const MONEY_COST_MULTIPLIER = 1.75;
|
||||||
const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND];
|
const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO];
|
||||||
const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL];
|
const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +131,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
// Init enemy
|
// Init enemy
|
||||||
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
@ -166,7 +167,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
||||||
await showEncounterText(scene, `${namespace}.attacked`);
|
await showEncounterText(scene, `${namespace}.attacked`);
|
||||||
|
|
||||||
// Init enemy
|
// Init enemy
|
||||||
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
|
|
@ -0,0 +1,549 @@
|
||||||
|
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { randSeedShuffle } from "#app/utils";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { Biome } from "#enums/biome";
|
||||||
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
|
import { Nature } from "#enums/nature";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { IEggOptions } from "#app/data/egg";
|
||||||
|
import { EggSourceType } from "#enums/egg-source-types";
|
||||||
|
import { EggTier } from "#enums/egg-type";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { achvs } from "#app/system/achv";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:expertPokemonBreeder";
|
||||||
|
|
||||||
|
const trainerNameKey = "trainerNames:expert_pokemon_breeder";
|
||||||
|
|
||||||
|
const FIRST_STAGE_EVOLUTION_WAVE = 30;
|
||||||
|
const SECOND_STAGE_EVOLUTION_WAVE = 45;
|
||||||
|
const FINAL_STAGE_EVOLUTION_WAVE = 60;
|
||||||
|
|
||||||
|
const FRIENDSHIP_ADDED = 20;
|
||||||
|
|
||||||
|
class BreederSpeciesEvolution {
|
||||||
|
species: Species;
|
||||||
|
evolution: number;
|
||||||
|
|
||||||
|
constructor(species: Species, evolution: number) {
|
||||||
|
this.species = species;
|
||||||
|
this.evolution = evolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||||
|
[Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.HAPPINY, new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.MAGBY, new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.ELEKID, new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.BUDEW, new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.MIME_JR, new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE)]
|
||||||
|
];
|
||||||
|
|
||||||
|
const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||||
|
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.JYNX],
|
||||||
|
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.IGGLYBUFF, new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.AZURILL, new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||||
|
[Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)]
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Expert Pokémon Breeder encounter.
|
||||||
|
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3818 | GitHub Issue #3818}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
|
.withScenePartySizeRequirement(4, 6, true) // Must have at least 4 legal pokemon in party
|
||||||
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
const waveIndex = scene.currentBattle.waveIndex;
|
||||||
|
// Calculates what trainers are available for battle in the encounter
|
||||||
|
|
||||||
|
// If player is in space biome, uses special "Space" version of the trainer
|
||||||
|
encounter.enemyPartyConfigs = [
|
||||||
|
getPartyConfig(scene)
|
||||||
|
];
|
||||||
|
|
||||||
|
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: cleffaSpecies.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 14,
|
||||||
|
y: -2,
|
||||||
|
yShadow: -2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "expert_pokemon_breeder",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -14,
|
||||||
|
y: 4,
|
||||||
|
yShadow: 2
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Determine the 3 pokemon the player can battle with
|
||||||
|
let partyCopy = scene.getParty().slice(0);
|
||||||
|
partyCopy = partyCopy
|
||||||
|
.filter(p => p.isAllowedInBattle())
|
||||||
|
.sort((a, b) => a.friendship - b.friendship);
|
||||||
|
|
||||||
|
const pokemon1 = partyCopy[0];
|
||||||
|
const pokemon2 = partyCopy[1];
|
||||||
|
const pokemon3 = partyCopy[2];
|
||||||
|
encounter.setDialogueToken("pokemon1Name", pokemon1.getNameToRender());
|
||||||
|
encounter.setDialogueToken("pokemon2Name", pokemon2.getNameToRender());
|
||||||
|
encounter.setDialogueToken("pokemon3Name", pokemon3.getNameToRender());
|
||||||
|
|
||||||
|
// Dialogue and egg calcs for Pokemon 1
|
||||||
|
const [pokemon1CommonEggs, pokemon1RareEggs] = calculateEggRewardsForPokemon(pokemon1);
|
||||||
|
let pokemon1Tooltip = getEncounterText(scene, `${namespace}.option.1.tooltip_base`)!;
|
||||||
|
if (pokemon1RareEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||||
|
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon1RareEggs", eggsText);
|
||||||
|
}
|
||||||
|
if (pokemon1CommonEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||||
|
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
|
||||||
|
}
|
||||||
|
encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip;
|
||||||
|
|
||||||
|
// Dialogue and egg calcs for Pokemon 2
|
||||||
|
const [pokemon2CommonEggs, pokemon2RareEggs] = calculateEggRewardsForPokemon(pokemon2);
|
||||||
|
let pokemon2Tooltip = getEncounterText(scene, `${namespace}.option.2.tooltip_base`)!;
|
||||||
|
if (pokemon2RareEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||||
|
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon2RareEggs", eggsText);
|
||||||
|
}
|
||||||
|
if (pokemon2CommonEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||||
|
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
|
||||||
|
}
|
||||||
|
encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip;
|
||||||
|
|
||||||
|
// Dialogue and egg calcs for Pokemon 3
|
||||||
|
const [pokemon3CommonEggs, pokemon3RareEggs] = calculateEggRewardsForPokemon(pokemon3);
|
||||||
|
let pokemon3Tooltip = getEncounterText(scene, `${namespace}.option.3.tooltip_base`)!;
|
||||||
|
if (pokemon3RareEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||||
|
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon3RareEggs", eggsText);
|
||||||
|
}
|
||||||
|
if (pokemon3CommonEggs > 0) {
|
||||||
|
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||||
|
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||||
|
encounter.setDialogueToken("pokemon3CommonEggs", eggsText);
|
||||||
|
}
|
||||||
|
encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip;
|
||||||
|
|
||||||
|
encounter.misc = {
|
||||||
|
pokemon1,
|
||||||
|
pokemon1CommonEggs,
|
||||||
|
pokemon1RareEggs,
|
||||||
|
pokemon2,
|
||||||
|
pokemon2CommonEggs,
|
||||||
|
pokemon2RareEggs,
|
||||||
|
pokemon3,
|
||||||
|
pokemon3CommonEggs,
|
||||||
|
pokemon3RareEggs
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
// Spawn battle with first pokemon
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc;
|
||||||
|
encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender());
|
||||||
|
const eggOptions = getEggOptions(scene, pokemon1CommonEggs, pokemon1RareEggs);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||||
|
|
||||||
|
// Remove all Pokemon from the party except the chosen Pokemon
|
||||||
|
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon1);
|
||||||
|
|
||||||
|
// Configure outro dialogue for egg rewards
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1CommonEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon1RareEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1RareEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give achievement if in Space biome
|
||||||
|
checkAchievement(scene);
|
||||||
|
// Give 20 friendship to the chosen pokemon
|
||||||
|
scene.currentBattle.mysteryEncounter!.misc.pokemon1.addFriendship(FRIENDSHIP_ADDED);
|
||||||
|
await restorePartyAndHeldItems(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
// Spawn battle with second pokemon
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc;
|
||||||
|
encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender());
|
||||||
|
const eggOptions = getEggOptions(scene, pokemon2CommonEggs, pokemon2RareEggs);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||||
|
|
||||||
|
// Remove all Pokemon from the party except the chosen Pokemon
|
||||||
|
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon2);
|
||||||
|
|
||||||
|
// Configure outro dialogue for egg rewards
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2CommonEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon2RareEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2RareEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give achievement if in Space biome
|
||||||
|
checkAchievement(scene);
|
||||||
|
// Give 20 friendship to the chosen pokemon
|
||||||
|
scene.currentBattle.mysteryEncounter!.misc.pokemon2.addFriendship(FRIENDSHIP_ADDED);
|
||||||
|
await restorePartyAndHeldItems(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
// Spawn battle with third pokemon
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc;
|
||||||
|
encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender());
|
||||||
|
const eggOptions = getEggOptions(scene, pokemon3CommonEggs, pokemon3RareEggs);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||||
|
|
||||||
|
// Remove all Pokemon from the party except the chosen Pokemon
|
||||||
|
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon3);
|
||||||
|
|
||||||
|
// Configure outro dialogue for egg rewards
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
speaker: trainerNameKey,
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3CommonEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (encounter.dialogueTokens.hasOwnProperty("pokemon3RareEggs")) {
|
||||||
|
encounter.dialogue.outro.push({
|
||||||
|
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3RareEggs"] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give achievement if in Space biome
|
||||||
|
checkAchievement(scene);
|
||||||
|
// Give 20 friendship to the chosen pokemon
|
||||||
|
scene.currentBattle.mysteryEncounter!.misc.pokemon3.addFriendship(FRIENDSHIP_ADDED);
|
||||||
|
await restorePartyAndHeldItems(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
// Bug type superfan trainer config
|
||||||
|
const waveIndex = scene.currentBattle.waveIndex;
|
||||||
|
const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone();
|
||||||
|
breederConfig.name = i18next.t(trainerNameKey);
|
||||||
|
|
||||||
|
// First mon is *always* this special cleffa
|
||||||
|
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
|
||||||
|
const baseConfig: EnemyPartyConfig = {
|
||||||
|
trainerType: TrainerType.EXPERT_POKEMON_BREEDER,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
nickname: i18next.t(`${namespace}.cleffa_1_nickname`),
|
||||||
|
species: getPokemonSpecies(cleffaSpecies),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 1, // Magic Guard
|
||||||
|
shiny: false,
|
||||||
|
nature: Nature.ADAMANT,
|
||||||
|
moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH],
|
||||||
|
ivs: [31, 31, 31, 31, 31, 31],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [Type.STEEL]) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.ATK]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 1 + Math.floor(waveIndex / 20), // +1 Protein every 20 waves
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 1 + Math.floor(waveIndex / 40), // +1 Carbos every 40 waves
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (scene.arena.biomeType === Biome.SPACE) {
|
||||||
|
// All 3 members always Cleffa line, but different configs
|
||||||
|
baseConfig.pokemonConfigs!.push({
|
||||||
|
nickname: i18next.t(`${namespace}.cleffa_2_nickname`),
|
||||||
|
species: getPokemonSpecies(cleffaSpecies),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 1, // Magic Guard
|
||||||
|
shiny: true,
|
||||||
|
variant: 1,
|
||||||
|
nature: Nature.MODEST,
|
||||||
|
moveSet: [Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT],
|
||||||
|
ivs: [31, 31, 31, 31, 31, 31]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nickname: i18next.t(`${namespace}.cleffa_3_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
|
||||||
|
species: getPokemonSpecies(cleffaSpecies),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 2, // Friend Guard / Unaware
|
||||||
|
shiny: true,
|
||||||
|
variant: 2,
|
||||||
|
nature: Nature.BOLD,
|
||||||
|
moveSet: [Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT],
|
||||||
|
ivs: [31, 31, 31, 31, 31, 31]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Second member from pool 1
|
||||||
|
const pool1Species = getSpeciesFromPool(POOL_1_POKEMON, waveIndex);
|
||||||
|
// Third member from pool 2
|
||||||
|
const pool2Species = getSpeciesFromPool(POOL_2_POKEMON, waveIndex);
|
||||||
|
|
||||||
|
baseConfig.pokemonConfigs!.push({
|
||||||
|
species: getPokemonSpecies(pool1Species),
|
||||||
|
isBoss: false,
|
||||||
|
ivs: [31, 31, 31, 31, 31, 31]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(pool2Species),
|
||||||
|
isBoss: false,
|
||||||
|
ivs: [31, 31, 31, 31, 31, 31]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species {
|
||||||
|
const poolCopy = speciesPool.slice(0);
|
||||||
|
randSeedShuffle(poolCopy);
|
||||||
|
const speciesEvolutions = poolCopy.pop()!.slice(0);
|
||||||
|
let speciesObject = speciesEvolutions.pop()!;
|
||||||
|
while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) {
|
||||||
|
speciesObject = speciesEvolutions.pop()!;
|
||||||
|
}
|
||||||
|
return speciesObject instanceof BreederSpeciesEvolution ? speciesObject.species : speciesObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] {
|
||||||
|
const bst = pokemon.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||||
|
// 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1)
|
||||||
|
const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18);
|
||||||
|
|
||||||
|
const rootSpecies = pokemon.species.getRootSpeciesId(true);
|
||||||
|
let pointsFromStarterTier = 0;
|
||||||
|
// 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0)
|
||||||
|
if (speciesStarters.hasOwnProperty(rootSpecies)) {
|
||||||
|
const starterTier = speciesStarters[rootSpecies];
|
||||||
|
pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum of 30 points
|
||||||
|
const totalPoints = Math.min(pointsFromStarterTier + pointsFromBst, 30);
|
||||||
|
|
||||||
|
// 1 Rare egg for every 6 points
|
||||||
|
const numRares = Math.floor(totalPoints / 6);
|
||||||
|
// 1 Common egg for every point leftover
|
||||||
|
const numCommons = totalPoints % 6;
|
||||||
|
|
||||||
|
return [numCommons, numRares];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) {
|
||||||
|
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(trainerNameKey);
|
||||||
|
const eggOptions: IEggOptions[] = [];
|
||||||
|
|
||||||
|
if (commonEggs > 0) {
|
||||||
|
for (let i = 0; i < commonEggs; i++) {
|
||||||
|
eggOptions.push({
|
||||||
|
scene,
|
||||||
|
pulled: false,
|
||||||
|
sourceType: EggSourceType.EVENT,
|
||||||
|
eggDescriptor: eggDescription,
|
||||||
|
tier: EggTier.COMMON
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rareEggs > 0) {
|
||||||
|
for (let i = 0; i < rareEggs; i++) {
|
||||||
|
eggOptions.push({
|
||||||
|
scene,
|
||||||
|
pulled: false,
|
||||||
|
sourceType: EggSourceType.EVENT,
|
||||||
|
eggDescriptor: eggDescription,
|
||||||
|
tier: EggTier.GREAT
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return eggOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
||||||
|
const party = scene.getParty();
|
||||||
|
const chosenIndex = party.indexOf(chosenPokemon);
|
||||||
|
party[chosenIndex] = party[0];
|
||||||
|
party[0] = chosenPokemon;
|
||||||
|
encounter.misc.originalParty = scene.getParty().slice(1);
|
||||||
|
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
|
||||||
|
.map(p => p.getHeldItems());
|
||||||
|
scene["party"] = [
|
||||||
|
chosenPokemon
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAchievement(scene: BattleScene) {
|
||||||
|
if (scene.arena.biomeType === Biome.SPACE) {
|
||||||
|
scene.validateAchv(achvs.BREEDERS_IN_SPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restorePartyAndHeldItems(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
// Restore original party
|
||||||
|
scene.getParty().push(...encounter.misc.originalParty);
|
||||||
|
|
||||||
|
// Restore held items
|
||||||
|
const originalHeldItems = encounter.misc.originalPartyHeldItems;
|
||||||
|
originalHeldItems.forEach(pokemonHeldItemsList => {
|
||||||
|
pokemonHeldItemsList.forEach(heldItem => {
|
||||||
|
scene.addModifier(heldItem, true, false, false, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, up
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -15,11 +15,15 @@ import PokemonData from "#app/system/pokemon-data";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:pokemonSalesman";
|
const namespace = "mysteryEncounter:pokemonSalesman";
|
||||||
|
|
||||||
const MAX_POKEMON_PRICE_MULTIPLIER = 6;
|
const MAX_POKEMON_PRICE_MULTIPLIER = 4;
|
||||||
|
|
||||||
|
/** Odds of shiny magikarp will be 1/value */
|
||||||
|
const SHINY_MAGIKARP_WEIGHT = 100;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pokemon Salesman encounter.
|
* Pokemon Salesman encounter.
|
||||||
|
@ -58,12 +62,12 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||||
const tries = 0;
|
const tries = 0;
|
||||||
|
|
||||||
// Reroll any species that don't have HAs
|
// Reroll any species that don't have HAs
|
||||||
while (isNullOrUndefined(species.abilityHidden) && tries < 5) {
|
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
|
||||||
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pokemon: PlayerPokemon;
|
let pokemon: PlayerPokemon;
|
||||||
if (isNullOrUndefined(species.abilityHidden) || randSeedInt(100) === 0) {
|
if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) {
|
||||||
// If no HA mon found or you roll 1%, give shiny Magikarp
|
// If no HA mon found or you roll 1%, give shiny Magikarp
|
||||||
species = getPokemonSpecies(Species.MAGIKARP);
|
species = getPokemonSpecies(Species.MAGIKARP);
|
||||||
const hiddenIndex = species.ability2 ? 2 : 1;
|
const hiddenIndex = species.ability2 ? 2 : 1;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounte
|
||||||
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { Nature } from "#app/data/nature";
|
import { Nature } from "#app/data/nature";
|
||||||
|
@ -36,6 +36,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
|
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
|
||||||
|
.withMaxAllowedEncounters(1)
|
||||||
.withHideWildIntroMessage(true)
|
.withHideWildIntroMessage(true)
|
||||||
.withAutoHideIntroVisuals(false)
|
.withAutoHideIntroVisuals(false)
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
|
@ -70,7 +71,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveModifier: 1,
|
||||||
disableSwitch: true,
|
disableSwitch: true,
|
||||||
pokemonConfigs: [
|
pokemonConfigs: [
|
||||||
{
|
{
|
||||||
|
@ -159,6 +160,11 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
|
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
|
||||||
await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, undefined, true);
|
await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, undefined, true);
|
||||||
|
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
}
|
||||||
|
];
|
||||||
setEncounterRewards(scene, { fillRemaining: true });
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -192,6 +198,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
ignorePp: true
|
ignorePp: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
encounter.dialogue.outro = [];
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { EnemyPartyConfig, generateModifierType, generateModifierTypeOption, ini
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -146,7 +146,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
|
||||||
|
|
||||||
// Give 10x Voucher
|
// Give 10x Voucher
|
||||||
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
|
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
|
||||||
scene.addModifier(newModifier);
|
await scene.addModifier(newModifier);
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattl
|
||||||
import { getNatureName, Nature } from "#app/data/nature";
|
import { getNatureName, Nature } from "#app/data/nature";
|
||||||
import { speciesStarters } from "#app/data/pokemon-species";
|
import { speciesStarters } from "#app/data/pokemon-species";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { AbilityAttr } from "#app/system/game-data";
|
import { AbilityAttr } from "#app/system/game-data";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
@ -11,8 +11,8 @@ import { isNullOrUndefined, randSeedShuffle } from "#app/utils";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -34,6 +34,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 unfainted pokemon in party
|
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 unfainted pokemon in party
|
||||||
|
.withFleeAllowed(false)
|
||||||
.withHideWildIntroMessage(true)
|
.withHideWildIntroMessage(true)
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
|
@ -97,12 +98,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||||
5
|
5
|
||||||
);
|
);
|
||||||
const modifiers = new ModifiersHolder();
|
const modifiers = new ModifiersHolder();
|
||||||
const config = getEnemyConfig(
|
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||||
scene,
|
|
||||||
playerPokemon,
|
|
||||||
segments,
|
|
||||||
modifiers
|
|
||||||
);
|
|
||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
const onBeforeRewardsPhase = () => {
|
||||||
|
@ -163,6 +159,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||||
// Add pokemon and mods back
|
// Add pokemon and mods back
|
||||||
scene.getParty().push(playerPokemon);
|
scene.getParty().push(playerPokemon);
|
||||||
for (const mod of modifiers.value) {
|
for (const mod of modifiers.value) {
|
||||||
|
mod.pokemonId = playerPokemon.id;
|
||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
}
|
}
|
||||||
scene.updateModifiers(true);
|
scene.updateModifiers(true);
|
||||||
|
@ -230,17 +227,9 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Spawn medium training session with chosen pokemon
|
// Spawn medium training session with chosen pokemon
|
||||||
// Every 40 waves, add +1 boss segment, capping at 6
|
// Every 40 waves, add +1 boss segment, capping at 6
|
||||||
const segments = Math.min(
|
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6);
|
||||||
2 + Math.floor(scene.currentBattle.waveIndex / 40),
|
|
||||||
6
|
|
||||||
);
|
|
||||||
const modifiers = new ModifiersHolder();
|
const modifiers = new ModifiersHolder();
|
||||||
const config = getEnemyConfig(
|
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||||
scene,
|
|
||||||
playerPokemon,
|
|
||||||
segments,
|
|
||||||
modifiers
|
|
||||||
);
|
|
||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
const onBeforeRewardsPhase = () => {
|
||||||
|
@ -377,6 +366,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||||
// Add pokemon and mods back
|
// Add pokemon and mods back
|
||||||
scene.getParty().push(playerPokemon);
|
scene.getParty().push(playerPokemon);
|
||||||
for (const mod of modifiers.value) {
|
for (const mod of modifiers.value) {
|
||||||
|
mod.pokemonId = playerPokemon.id;
|
||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
}
|
}
|
||||||
scene.updateModifiers(true);
|
scene.updateModifiers(true);
|
||||||
|
@ -410,10 +400,12 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segmen
|
||||||
playerPokemon.resetSummonData();
|
playerPokemon.resetSummonData();
|
||||||
|
|
||||||
// Passes modifiers by reference
|
// Passes modifiers by reference
|
||||||
modifiers.value = playerPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
|
modifiers.value = playerPokemon.getHeldItems();
|
||||||
const modifierConfigs = modifiers.value.map((mod) => {
|
const modifierConfigs = modifiers.value.map((mod) => {
|
||||||
return {
|
return {
|
||||||
modifier: mod
|
modifier: mod.clone(),
|
||||||
|
isTransferable: false,
|
||||||
|
stackCount: mod.stackCount
|
||||||
};
|
};
|
||||||
}) as HeldModifierConfig[];
|
}) as HeldModifierConfig[];
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleW
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -67,7 +67,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||||
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH]
|
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH]
|
||||||
};
|
};
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveModifier: 1,
|
||||||
pokemonConfigs: [pokemonConfig],
|
pokemonConfigs: [pokemonConfig],
|
||||||
disableSwitch: true
|
disableSwitch: true
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,8 @@ import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getPartyLuckValue } from "#app/modifier/modifier-type";
|
import { getPartyLuckValue } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MoveRequirement, PersistentModifierRequirement } from "../mystery-encounter-requirements";
|
import { MoveRequirement, PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { Type } from "#app/data/type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
import { leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -14,7 +14,7 @@ import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, Pokemo
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { speciesEggMoves } from "#app/data/egg-moves";
|
import { speciesEggMoves } from "#app/data/egg-moves";
|
||||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
|
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
|
||||||
|
@ -130,12 +130,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOnVisualsStart((scene: BattleScene) => {
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
// Change the bgm
|
scene.fadeAndSwitchBgm("mystery_encounter_weird_dream");
|
||||||
scene.fadeOutBgm(3000, false);
|
|
||||||
scene.time.delayedCall(3000, () => {
|
|
||||||
scene.playBgm("mystery_encounter_weird_dream");
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withOption(
|
.withOption(
|
||||||
|
@ -340,7 +335,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
||||||
const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false);
|
const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false);
|
||||||
if (newStarterUnlocked) {
|
if (newStarterUnlocked) {
|
||||||
atLeastOneNewStarter = true;
|
atLeastOneNewStarter = true;
|
||||||
queueEncounterMessage(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
|
await showEncounterText(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Moves } from "#app/enums/moves";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { Type } from "../type";
|
import { Type } from "../type";
|
||||||
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements";
|
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
|
@ -25,6 +25,9 @@ export interface EncounterStartOfBattleEffect {
|
||||||
followUp?: boolean;
|
followUp?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_MAX_ALLOWED_ENCOUNTERS = 2;
|
||||||
|
const DEFAULT_MAX_ALLOWED_ROGUE_ENCOUNTERS = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by {@linkcode MysteryEncounterBuilder} class to define required/optional properties on the {@linkcode MysteryEncounter} class when building.
|
* Used by {@linkcode MysteryEncounterBuilder} class to define required/optional properties on the {@linkcode MysteryEncounter} class when building.
|
||||||
*
|
*
|
||||||
|
@ -42,6 +45,7 @@ export interface IMysteryEncounter {
|
||||||
autoHideIntroVisuals: boolean;
|
autoHideIntroVisuals: boolean;
|
||||||
enterIntroVisualsFromRight: boolean;
|
enterIntroVisualsFromRight: boolean;
|
||||||
catchAllowed: boolean;
|
catchAllowed: boolean;
|
||||||
|
fleeAllowed: boolean;
|
||||||
continuousEncounter: boolean;
|
continuousEncounter: boolean;
|
||||||
maxAllowedEncounters: number;
|
maxAllowedEncounters: number;
|
||||||
hasBattleAnimationsWithoutTargets: boolean;
|
hasBattleAnimationsWithoutTargets: boolean;
|
||||||
|
@ -110,6 +114,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
* Default false
|
* Default false
|
||||||
*/
|
*/
|
||||||
catchAllowed: boolean;
|
catchAllowed: boolean;
|
||||||
|
/**
|
||||||
|
* If true, allows fleeing from a wild encounter (trainer battle MEs auto-disable fleeing)
|
||||||
|
* Default true
|
||||||
|
*/
|
||||||
|
fleeAllowed: boolean;
|
||||||
/**
|
/**
|
||||||
* If true, encounter will continuously run through multiple battles/puzzles/etc. instead of going to next wave
|
* If true, encounter will continuously run through multiple battles/puzzles/etc. instead of going to next wave
|
||||||
* MUST EVENTUALLY BE DISABLED TO CONTINUE TO NEXT WAVE
|
* MUST EVENTUALLY BE DISABLED TO CONTINUE TO NEXT WAVE
|
||||||
|
@ -246,8 +255,8 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON;
|
this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON;
|
||||||
this.dialogue = this.dialogue ?? {};
|
this.dialogue = this.dialogue ?? {};
|
||||||
this.spriteConfigs = this.spriteConfigs ? [...this.spriteConfigs] : [];
|
this.spriteConfigs = this.spriteConfigs ? [...this.spriteConfigs] : [];
|
||||||
// Default max is 1 for ROGUE encounters, 3 for others
|
// Default max is 1 for ROGUE encounters, 2 for others
|
||||||
this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? 1 : 3;
|
this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? DEFAULT_MAX_ALLOWED_ROGUE_ENCOUNTERS : DEFAULT_MAX_ALLOWED_ENCOUNTERS;
|
||||||
this.encounterMode = MysteryEncounterMode.DEFAULT;
|
this.encounterMode = MysteryEncounterMode.DEFAULT;
|
||||||
this.requirements = this.requirements ? this.requirements : [];
|
this.requirements = this.requirements ? this.requirements : [];
|
||||||
this.hideBattleIntroMessage = this.hideBattleIntroMessage ?? false;
|
this.hideBattleIntroMessage = this.hideBattleIntroMessage ?? false;
|
||||||
|
@ -520,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
enterIntroVisualsFromRight: boolean = false;
|
enterIntroVisualsFromRight: boolean = false;
|
||||||
continuousEncounter: boolean = false;
|
continuousEncounter: boolean = false;
|
||||||
catchAllowed: boolean = false;
|
catchAllowed: boolean = false;
|
||||||
|
fleeAllowed: boolean = true;
|
||||||
lockEncounterRewardTiers: boolean = false;
|
lockEncounterRewardTiers: boolean = false;
|
||||||
startOfBattleEffectsComplete: boolean = false;
|
startOfBattleEffectsComplete: boolean = false;
|
||||||
hasBattleAnimationsWithoutTargets: boolean = false;
|
hasBattleAnimationsWithoutTargets: boolean = false;
|
||||||
|
@ -580,8 +590,8 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
* There should be at least 2 options defined and no more than 4.
|
* There should be at least 2 options defined and no more than 4.
|
||||||
* If complex use {@linkcode MysteryEncounterBuilder.withOption}
|
* If complex use {@linkcode MysteryEncounterBuilder.withOption}
|
||||||
*
|
*
|
||||||
* @param dialogue - {@linkcode OptionTextDisplay}
|
* @param dialogue {@linkcode OptionTextDisplay}
|
||||||
* @param callback - {@linkcode OptionPhaseCallback}
|
* @param callback {@linkcode OptionPhaseCallback}
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withSimpleDexProgressOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick<IMysteryEncounter, "options"> {
|
withSimpleDexProgressOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick<IMysteryEncounter, "options"> {
|
||||||
|
@ -732,7 +742,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
*
|
*
|
||||||
* @param min min wave (or exact size if only min is given)
|
* @param min min wave (or exact size if only min is given)
|
||||||
* @param max optional max size. If not given, defaults to min => exact wave
|
* @param max optional max size. If not given, defaults to min => exact wave
|
||||||
* @param excludeFainted - if true, only counts unfainted mons
|
* @param excludeFainted if true, only counts unfainted mons
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
||||||
|
@ -798,7 +808,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
||||||
* It may be better to programmatically set doEncounterRewards elsewhere.
|
* It may be better to programmatically set doEncounterRewards elsewhere.
|
||||||
* There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set rewards
|
* There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set rewards
|
||||||
* @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter
|
* @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
||||||
|
@ -812,7 +822,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
||||||
* It may be better to programmatically set doEncounterExp elsewhere.
|
* It may be better to programmatically set doEncounterExp elsewhere.
|
||||||
* There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set rewards
|
* There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set rewards
|
||||||
* @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter
|
* @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
||||||
|
@ -823,7 +833,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
* Can be used to perform init logic before intro visuals are shown and before the MysteryEncounterPhase begins
|
* Can be used to perform init logic before intro visuals are shown and before the MysteryEncounterPhase begins
|
||||||
* Useful for performing things like procedural generation of intro sprites, etc.
|
* Useful for performing things like procedural generation of intro sprites, etc.
|
||||||
*
|
*
|
||||||
* @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase
|
* @param onInit Synchronous callback function to perform as soon as the encounter is selected for the next phase
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
|
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
|
||||||
|
@ -833,7 +843,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in
|
* Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in
|
||||||
*
|
*
|
||||||
* @param onVisualsStart - synchronous callback function to perform as soon as the enemy field finishes sliding in
|
* @param onVisualsStart Synchronous callback function to perform as soon as the enemy field finishes sliding in
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
||||||
|
@ -843,7 +853,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Can set whether catching is allowed or not on the encounter
|
* Can set whether catching is allowed or not on the encounter
|
||||||
* This flag can also be programmatically set inside option event functions or elsewhere
|
* This flag can also be programmatically set inside option event functions or elsewhere
|
||||||
* @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter
|
* @param catchAllowed If `true`, allows enemy pokemon to be caught during the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> {
|
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> {
|
||||||
|
@ -851,7 +861,16 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter
|
* Can set whether fleeing is allowed or not on the encounter
|
||||||
|
* @param fleeAllowed If `false`, prevents fleeing from a wild battle (trainer battle MEs already have flee disabled)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withFleeAllowed(fleeAllowed: boolean): this & Required<Pick<IMysteryEncounter, "fleeAllowed">> {
|
||||||
|
return Object.assign(this, { fleeAllowed });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hideBattleIntroMessage If `true`, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> {
|
withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> {
|
||||||
|
@ -859,7 +878,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter
|
* @param autoHideIntroVisuals If `false`, will not hide the intro visuals that are displayed at the beginning of encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> {
|
withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> {
|
||||||
|
@ -867,7 +886,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param enterIntroVisualsFromRight - If true, will slide in intro visuals from the right side of the screen. If false, slides in from left, as normal
|
* @param enterIntroVisualsFromRight If `true`, will slide in intro visuals from the right side of the screen. If false, slides in from left, as normal
|
||||||
* Default false
|
* Default false
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
@ -878,7 +897,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Add a title for the encounter
|
* Add a title for the encounter
|
||||||
*
|
*
|
||||||
* @param title - title of the encounter
|
* @param title Title of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withTitle(title: string): this {
|
withTitle(title: string): this {
|
||||||
|
@ -898,7 +917,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Add a description of the encounter
|
* Add a description of the encounter
|
||||||
*
|
*
|
||||||
* @param description - description of the encounter
|
* @param description Description of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withDescription(description: string): this {
|
withDescription(description: string): this {
|
||||||
|
@ -918,7 +937,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Add a query for the encounter
|
* Add a query for the encounter
|
||||||
*
|
*
|
||||||
* @param query - query to use for the encounter
|
* @param query Query to use for the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withQuery(query: string): this {
|
withQuery(query: string): this {
|
||||||
|
@ -938,7 +957,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
/**
|
/**
|
||||||
* Add outro dialogue/s for the encounter
|
* Add outro dialogue/s for the encounter
|
||||||
*
|
*
|
||||||
* @param dialogue - outro dialogue/s
|
* @param dialogue Outro dialogue(s)
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []): this {
|
withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []): this {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounter
|
||||||
import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter";
|
import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter";
|
||||||
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
||||||
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
|
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
|
||||||
|
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
||||||
|
@ -184,7 +185,8 @@ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [
|
||||||
MysteryEncounterType.SHADY_VITAMIN_DEALER,
|
MysteryEncounterType.SHADY_VITAMIN_DEALER,
|
||||||
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
||||||
MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE,
|
MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE,
|
||||||
MysteryEncounterType.THE_WINSTRATE_CHALLENGE
|
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
||||||
|
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
||||||
];
|
];
|
||||||
|
|
||||||
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
||||||
|
@ -238,7 +240,6 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
MysteryEncounterType.SAFARI_ZONE,
|
MysteryEncounterType.SAFARI_ZONE,
|
||||||
MysteryEncounterType.ABSOLUTE_AVARICE
|
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||||
]],
|
]],
|
||||||
|
|
||||||
[Biome.SEA, [
|
[Biome.SEA, [
|
||||||
MysteryEncounterType.LOST_AT_SEA
|
MysteryEncounterType.LOST_AT_SEA
|
||||||
]],
|
]],
|
||||||
|
@ -275,7 +276,9 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
[Biome.ABYSS, [
|
[Biome.ABYSS, [
|
||||||
MysteryEncounterType.DANCING_LESSONS
|
MysteryEncounterType.DANCING_LESSONS
|
||||||
]],
|
]],
|
||||||
[Biome.SPACE, []],
|
[Biome.SPACE, [
|
||||||
|
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
||||||
|
]],
|
||||||
[Biome.CONSTRUCTION_SITE, []],
|
[Biome.CONSTRUCTION_SITE, []],
|
||||||
[Biome.JUNGLE, [
|
[Biome.JUNGLE, [
|
||||||
MysteryEncounterType.SAFARI_ZONE
|
MysteryEncounterType.SAFARI_ZONE
|
||||||
|
@ -319,6 +322,7 @@ export function initMysteryEncounters() {
|
||||||
allMysteryEncounters[MysteryEncounterType.FUN_AND_GAMES] = FunAndGamesEncounter;
|
allMysteryEncounters[MysteryEncounterType.FUN_AND_GAMES] = FunAndGamesEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.UNCOMMON_BREED] = UncommonBreedEncounter;
|
allMysteryEncounters[MysteryEncounterType.UNCOMMON_BREED] = UncommonBreedEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.GLOBAL_TRADE_SYSTEM] = GlobalTradeSystemEncounter;
|
allMysteryEncounters[MysteryEncounterType.GLOBAL_TRADE_SYSTEM] = GlobalTradeSystemEncounter;
|
||||||
|
allMysteryEncounters[MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER] = TheExpertPokemonBreederEncounter;
|
||||||
|
|
||||||
// Add extreme encounters to biome map
|
// Add extreme encounters to biome map
|
||||||
extremeBiomeEncounters.forEach(encounter => {
|
extremeBiomeEncounters.forEach(encounter => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import BattleScene from "#app/battle-scene";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
import { EncounterPokemonRequirement } from "../mystery-encounter-requirements";
|
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@linkcode CanLearnMoveRequirement} options
|
* {@linkcode CanLearnMoveRequirement} options
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { biomeLinks, BiomePoolTier } from "#app/data/biomes";
|
||||||
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
import Pokemon, { AiType, FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
||||||
import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
@ -36,6 +36,7 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||||
|
import { Variant } from "#app/data/variant";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animates exclamation sprite over trainer's head at start of encounter
|
* Animates exclamation sprite over trainer's head at start of encounter
|
||||||
|
@ -67,6 +68,7 @@ export function doTrainerExclamation(scene: BattleScene) {
|
||||||
export interface EnemyPokemonConfig {
|
export interface EnemyPokemonConfig {
|
||||||
species: PokemonSpecies;
|
species: PokemonSpecies;
|
||||||
isBoss: boolean;
|
isBoss: boolean;
|
||||||
|
nickname?: string;
|
||||||
bossSegments?: number;
|
bossSegments?: number;
|
||||||
bossSegmentModifier?: number; // Additive to the determined segment number
|
bossSegmentModifier?: number; // Additive to the determined segment number
|
||||||
mysteryEncounterPokemonData?: MysteryEncounterPokemonData;
|
mysteryEncounterPokemonData?: MysteryEncounterPokemonData;
|
||||||
|
@ -79,27 +81,32 @@ export interface EnemyPokemonConfig {
|
||||||
nature?: Nature;
|
nature?: Nature;
|
||||||
ivs?: [number, number, number, number, number, number];
|
ivs?: [number, number, number, number, number, number];
|
||||||
shiny?: boolean;
|
shiny?: boolean;
|
||||||
|
/** Is only checked if Pokemon is shiny */
|
||||||
|
variant?: Variant;
|
||||||
/** Can set just the status, or pass a timer on the status turns */
|
/** Can set just the status, or pass a timer on the status turns */
|
||||||
status?: StatusEffect | [StatusEffect, number];
|
status?: StatusEffect | [StatusEffect, number];
|
||||||
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
||||||
modifierConfigs?: HeldModifierConfig[];
|
modifierConfigs?: HeldModifierConfig[];
|
||||||
tags?: BattlerTagType[];
|
tags?: BattlerTagType[];
|
||||||
dataSource?: PokemonData;
|
dataSource?: PokemonData;
|
||||||
|
aiType?: AiType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnemyPartyConfig {
|
export interface EnemyPartyConfig {
|
||||||
/** Formula for enemy: level += waveIndex / 10 * levelAdditive */
|
/** Formula for enemy level: level += waveIndex / 10 * levelAdditiveModifier */
|
||||||
levelAdditiveMultiplier?: number;
|
levelAdditiveModifier?: number;
|
||||||
doubleBattle?: boolean;
|
doubleBattle?: boolean;
|
||||||
/** Generates trainer battle solely off trainer type */
|
/** Generates trainer battle solely off trainer type */
|
||||||
trainerType?: TrainerType;
|
trainerType?: TrainerType;
|
||||||
/** More customizable option for configuring trainer battle */
|
/** More customizable option for configuring trainer battle */
|
||||||
trainerConfig?: TrainerConfig;
|
trainerConfig?: TrainerConfig;
|
||||||
pokemonConfigs?: EnemyPokemonConfig[];
|
pokemonConfigs?: EnemyPokemonConfig[];
|
||||||
/** True for female trainer, false for male */
|
/** `true` for female trainer, false for male */
|
||||||
female?: boolean;
|
female?: boolean;
|
||||||
/** True will prevent player from switching */
|
/** `true` will prevent player from switching */
|
||||||
disableSwitch?: boolean;
|
disableSwitch?: boolean;
|
||||||
|
/** `true` or leaving undefined will increment dex seen count for the encounter battle, `false` will not */
|
||||||
|
countAsSeen?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,10 +163,10 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||||
|
|
||||||
// ME levels are modified by an additive value that scales with wave index
|
// ME levels are modified by an additive value that scales with wave index
|
||||||
// Base scaling: Every 10 waves, modifier gets +1 level
|
// Base scaling: Every 10 waves, modifier gets +1 level
|
||||||
// This can be amplified or counteracted by setting levelAdditiveMultiplier in config
|
// This can be amplified or counteracted by setting levelAdditiveModifier in config
|
||||||
// levelAdditiveMultiplier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
|
// levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
|
||||||
// Leaving null/undefined will disable level scaling
|
// Leaving null/undefined will disable level scaling
|
||||||
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveMultiplier) ? partyConfig.levelAdditiveMultiplier! : 0;
|
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier! : 0;
|
||||||
const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0);
|
const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0);
|
||||||
battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
|
battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
|
||||||
|
|
||||||
|
@ -210,13 +217,18 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||||
enemyPokemon.resetSummonData();
|
enemyPokemon.resetSummonData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loaded) {
|
if (!loaded && isNullOrUndefined(partyConfig.countAsSeen) || partyConfig.countAsSeen) {
|
||||||
scene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig));
|
scene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) {
|
if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) {
|
||||||
const config = partyConfig.pokemonConfigs[e];
|
const config = partyConfig.pokemonConfigs[e];
|
||||||
|
|
||||||
|
// Set form
|
||||||
|
if (!isNullOrUndefined(config.nickname)) {
|
||||||
|
enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname!)));
|
||||||
|
}
|
||||||
|
|
||||||
// Generate new id, reset status and HP in case using data source
|
// Generate new id, reset status and HP in case using data source
|
||||||
if (config.dataSource) {
|
if (config.dataSource) {
|
||||||
enemyPokemon.id = Utils.randSeedInt(4294967296);
|
enemyPokemon.id = Utils.randSeedInt(4294967296);
|
||||||
|
@ -232,6 +244,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||||
enemyPokemon.shiny = config.shiny!;
|
enemyPokemon.shiny = config.shiny!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Variant
|
||||||
|
if (enemyPokemon.shiny && !isNullOrUndefined(config.variant)) {
|
||||||
|
enemyPokemon.variant = config.variant!;
|
||||||
|
}
|
||||||
|
|
||||||
// Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.)
|
// Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.)
|
||||||
if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) {
|
if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) {
|
||||||
enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!;
|
enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!;
|
||||||
|
@ -286,6 +303,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||||
enemyPokemon.summonData.gender = config.gender!;
|
enemyPokemon.summonData.gender = config.gender!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set AI type
|
||||||
|
if (!isNullOrUndefined(config.aiType)) {
|
||||||
|
enemyPokemon.aiType = config.aiType!;
|
||||||
|
}
|
||||||
|
|
||||||
// Set moves
|
// Set moves
|
||||||
if (config?.moveSet && config.moveSet.length > 0) {
|
if (config?.moveSet && config.moveSet.length > 0) {
|
||||||
const moves = config.moveSet.map(m => new PokemonMove(m));
|
const moves = config.moveSet.map(m => new PokemonMove(m));
|
||||||
|
@ -307,6 +329,9 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||||
// Requires re-priming summon data to update everything properly
|
// Requires re-priming summon data to update everything properly
|
||||||
enemyPokemon.primeSummonData(enemyPokemon.summonData);
|
enemyPokemon.primeSummonData(enemyPokemon.summonData);
|
||||||
|
|
||||||
|
if (enemyPokemon.isShiny() && !enemyPokemon["shinySparkle"]) {
|
||||||
|
enemyPokemon.initShinySparkle();
|
||||||
|
}
|
||||||
enemyPokemon.initBattleInfo();
|
enemyPokemon.initBattleInfo();
|
||||||
enemyPokemon.getBattleInfo().initInfo(enemyPokemon);
|
enemyPokemon.getBattleInfo().initInfo(enemyPokemon);
|
||||||
enemyPokemon.generateName();
|
enemyPokemon.generateName();
|
||||||
|
@ -702,19 +727,19 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
||||||
if (encounter.continuousEncounter || doNotContinue) {
|
if (encounter.continuousEncounter || doNotContinue) {
|
||||||
return;
|
return;
|
||||||
} else if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
|
} else if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
|
||||||
scene.pushPhase(new EggLapsePhase(scene));
|
|
||||||
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
||||||
|
scene.pushPhase(new EggLapsePhase(scene));
|
||||||
} else if (!scene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) {
|
} else if (!scene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) {
|
||||||
scene.pushPhase(new BattleEndPhase(scene));
|
scene.pushPhase(new BattleEndPhase(scene));
|
||||||
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||||
scene.pushPhase(new TrainerVictoryPhase(scene));
|
scene.pushPhase(new TrainerVictoryPhase(scene));
|
||||||
}
|
}
|
||||||
if (scene.gameMode.isEndless || !scene.gameMode.isWaveFinal(scene.currentBattle.waveIndex)) {
|
if (scene.gameMode.isEndless || !scene.gameMode.isWaveFinal(scene.currentBattle.waveIndex)) {
|
||||||
|
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
||||||
if (!encounter.doContinueEncounter) {
|
if (!encounter.doContinueEncounter) {
|
||||||
// Only lapse eggs once for multi-battle encounters
|
// Only lapse eggs once for multi-battle encounters
|
||||||
scene.pushPhase(new EggLapsePhase(scene));
|
scene.pushPhase(new EggLapsePhase(scene));
|
||||||
}
|
}
|
||||||
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { PermanentStat } from "#enums/stat";
|
import { PermanentStat } from "#enums/stat";
|
||||||
import { VictoryPhase } from "#app/phases/victory-phase";
|
import { VictoryPhase } from "#app/phases/victory-phase";
|
||||||
|
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
||||||
|
|
||||||
|
/** Will give +1 level every 10 waves */
|
||||||
|
export const STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the sprite key and file root for a given PokemonSpecies (accounts for gender, shiny, variants, forms, and experimental)
|
* Gets the sprite key and file root for a given PokemonSpecies (accounts for gender, shiny, variants, forms, and experimental)
|
||||||
|
@ -289,10 +293,12 @@ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: numb
|
||||||
*/
|
*/
|
||||||
export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) {
|
export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) {
|
||||||
// Check if the Pokemon has max stacks of that item already
|
// Check if the Pokemon has max stacks of that item already
|
||||||
|
const modifier = modType.newModifier(pokemon);
|
||||||
const existing = scene.findModifier(m => (
|
const existing = scene.findModifier(m => (
|
||||||
m instanceof PokemonHeldItemModifier &&
|
m instanceof PokemonHeldItemModifier &&
|
||||||
m.type.id === modType.id &&
|
m.type.id === modType.id &&
|
||||||
m.pokemonId === pokemon.id
|
m.pokemonId === pokemon.id &&
|
||||||
|
m.matchType(modifier)
|
||||||
)) as PokemonHeldItemModifier;
|
)) as PokemonHeldItemModifier;
|
||||||
|
|
||||||
// At max stacks
|
// At max stacks
|
||||||
|
@ -305,7 +311,6 @@ export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokem
|
||||||
return applyModifierTypeToPlayerPokemon(scene, pokemon, fallbackModifierType);
|
return applyModifierTypeToPlayerPokemon(scene, pokemon, fallbackModifierType);
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifier = modType.newModifier(pokemon);
|
|
||||||
await scene.addModifier(modifier, false, false, false, true);
|
await scene.addModifier(modifier, false, false, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +332,7 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
|
||||||
const _3m = 3 * pokemon.getMaxHp();
|
const _3m = 3 * pokemon.getMaxHp();
|
||||||
const _2h = 2 * pokemon.hp;
|
const _2h = 2 * pokemon.hp;
|
||||||
const catchRate = pokemon.species.catchRate;
|
const catchRate = pokemon.species.catchRate;
|
||||||
const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType);
|
const pokeballMultiplier = getPokeballCatchMultiplier(pokeballType);
|
||||||
const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
|
const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
|
||||||
const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
|
const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
|
||||||
ballTwitchRate = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x)));
|
ballTwitchRate = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x)));
|
||||||
|
@ -501,8 +506,6 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
|
||||||
* @param isObtain
|
* @param isObtain
|
||||||
*/
|
*/
|
||||||
export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise<void> {
|
export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise<void> {
|
||||||
scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true));
|
|
||||||
|
|
||||||
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||||
|
|
||||||
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
|
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
|
||||||
|
@ -528,6 +531,11 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const doPokemonCatchMenu = () => {
|
const doPokemonCatchMenu = () => {
|
||||||
const end = () => {
|
const end = () => {
|
||||||
|
// Ensure the pokemon is in the enemy party in all situations
|
||||||
|
if (!scene.getEnemyParty().some(p => p.id === pokemon.id)) {
|
||||||
|
scene.getEnemyParty().push(pokemon);
|
||||||
|
}
|
||||||
|
scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true));
|
||||||
scene.pokemonInfoContainer.hide();
|
scene.pokemonInfoContainer.hide();
|
||||||
if (pokeball) {
|
if (pokeball) {
|
||||||
removePb(scene, pokeball);
|
removePb(scene, pokeball);
|
||||||
|
@ -539,8 +547,8 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||||
scene.field.remove(pokemon, true);
|
scene.field.remove(pokemon, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const addToParty = () => {
|
const addToParty = (slotIndex?: number) => {
|
||||||
const newPokemon = pokemon.addToParty(pokeballType);
|
const newPokemon = pokemon.addToParty(pokeballType, slotIndex);
|
||||||
const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
||||||
if (scene.getParty().filter(p => p.isShiny()).length === 6) {
|
if (scene.getParty().filter(p => p.isShiny()).length === 6) {
|
||||||
scene.validateAchv(achvs.SHINY_PARTY);
|
scene.validateAchv(achvs.SHINY_PARTY);
|
||||||
|
@ -559,12 +567,19 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||||
if (scene.getParty().length === 6) {
|
if (scene.getParty().length === 6) {
|
||||||
const promptRelease = () => {
|
const promptRelease = () => {
|
||||||
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
||||||
scene.pokemonInfoContainer.makeRoomForConfirmUi();
|
scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
|
||||||
scene.ui.setMode(Mode.CONFIRM, () => {
|
scene.ui.setMode(Mode.CONFIRM, () => {
|
||||||
scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: number, _option: PartyOption) => {
|
const newPokemon = scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon);
|
||||||
|
scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => {
|
||||||
|
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||||
|
promptRelease();
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
}, () => {
|
||||||
|
scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: integer, _option: PartyOption) => {
|
||||||
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||||
if (slotIndex < 6) {
|
if (slotIndex < 6) {
|
||||||
addToParty();
|
addToParty(slotIndex);
|
||||||
} else {
|
} else {
|
||||||
promptRelease();
|
promptRelease();
|
||||||
}
|
}
|
||||||
|
@ -575,7 +590,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||||
removePokemon();
|
removePokemon();
|
||||||
end();
|
end();
|
||||||
});
|
});
|
||||||
});
|
}, "fullParty");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
promptRelease();
|
promptRelease();
|
||||||
|
@ -711,13 +726,50 @@ export function getGoldenBugNetSpecies(): PokemonSpecies {
|
||||||
const roll = randSeedInt(totalWeight);
|
const roll = randSeedInt(totalWeight);
|
||||||
|
|
||||||
let w = 0;
|
let w = 0;
|
||||||
for (const species of GOLDEN_BUG_NET_SPECIES_POOL) {
|
for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) {
|
||||||
w += species[1];
|
w += speciesWeightPair[1];
|
||||||
if (roll < w) {
|
if (roll < w) {
|
||||||
return getPokemonSpecies(species);
|
return getPokemonSpecies(speciesWeightPair[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults to Scyther
|
// Defaults to Scyther
|
||||||
return getPokemonSpecies(Species.SCYTHER);
|
return getPokemonSpecies(Species.SCYTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Pokemon level for a given wave, with an option to increase/decrease by a scaling modifier
|
||||||
|
* @param scene
|
||||||
|
* @param levelAdditiveModifier Default 0. will add +(1 level / 10 waves * levelAdditiveModifier) to the level calculation
|
||||||
|
*/
|
||||||
|
export function getEncounterPokemonLevelForWave(scene: BattleScene, levelAdditiveModifier: number = 0) {
|
||||||
|
const currentBattle = scene.currentBattle;
|
||||||
|
// Default to use the first generated level from enemyLevels, or generate a new one if it DNE
|
||||||
|
const baseLevel = currentBattle.enemyLevels && currentBattle.enemyLevels?.length > 0 ? currentBattle.enemyLevels[0] : currentBattle.getLevelForWave();
|
||||||
|
|
||||||
|
// Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier
|
||||||
|
return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addPokemonDataToDexAndValidateAchievements(scene: BattleScene, pokemon: PlayerPokemon) {
|
||||||
|
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||||
|
|
||||||
|
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
|
||||||
|
scene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemon.species.subLegendary) {
|
||||||
|
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemon.species.legendary) {
|
||||||
|
scene.validateAchv(achvs.CATCH_LEGENDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemon.species.mythical) {
|
||||||
|
scene.validateAchv(achvs.CATCH_MYTHICAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
|
||||||
|
return scene.gameData.setPokemonCaught(pokemon, true, false, false);
|
||||||
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
|
||||||
scene.time.delayedCall(1000, () => {
|
scene.time.delayedCall(1000, () => {
|
||||||
pokemonEvoTintSprite.setScale(0.25);
|
pokemonEvoTintSprite.setScale(0.25);
|
||||||
pokemonEvoTintSprite.setVisible(true);
|
pokemonEvoTintSprite.setVisible(true);
|
||||||
doCycle(scene, 2, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => {
|
doCycle(scene, 1.5, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => {
|
||||||
pokemonEvoSprite.setVisible(true);
|
pokemonEvoSprite.setVisible(true);
|
||||||
doCircleInward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset);
|
doCircleInward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset);
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
|
||||||
delay: 150,
|
delay: 150,
|
||||||
easing: "Sine.easeIn",
|
easing: "Sine.easeIn",
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
scene.time.delayedCall(2500, () => {
|
scene.time.delayedCall(3000, () => {
|
||||||
resolve();
|
resolve();
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
targets: pokemonEvoSprite,
|
targets: pokemonEvoSprite,
|
||||||
|
|
|
@ -1105,8 +1105,16 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
|
||||||
return getWavePartyTemplate(scene, trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5);
|
return getWavePartyTemplate(scene, trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
|
/**
|
||||||
return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => {
|
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
|
||||||
|
* Then adds Pokemon to scene.
|
||||||
|
* @param speciesPool
|
||||||
|
* @param trainerSlot
|
||||||
|
* @param ignoreEvolution
|
||||||
|
* @param postProcess
|
||||||
|
*/
|
||||||
|
export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
||||||
|
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
||||||
let species = Utils.randSeedItem(speciesPool);
|
let species = Utils.randSeedItem(speciesPool);
|
||||||
if (!ignoreEvolution) {
|
if (!ignoreEvolution) {
|
||||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
||||||
|
@ -2302,6 +2310,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||||
.setMoneyMultiplier(2)
|
.setMoneyMultiplier(2)
|
||||||
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))),
|
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))),
|
||||||
[TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
|
[TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
|
||||||
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE))
|
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE)),
|
||||||
|
[TrainerType.EXPERT_POKEMON_BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(3).setEncounterBgm(TrainerType.ACE_TRAINER)
|
||||||
|
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.STRONG))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,5 +28,6 @@ export enum MysteryEncounterType {
|
||||||
BUG_TYPE_SUPERFAN,
|
BUG_TYPE_SUPERFAN,
|
||||||
FUN_AND_GAMES,
|
FUN_AND_GAMES,
|
||||||
UNCOMMON_BREED,
|
UNCOMMON_BREED,
|
||||||
GLOBAL_TRADE_SYSTEM
|
GLOBAL_TRADE_SYSTEM,
|
||||||
|
THE_EXPERT_POKEMON_BREEDER
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ export enum TrainerType {
|
||||||
VICKY,
|
VICKY,
|
||||||
VITO,
|
VITO,
|
||||||
BUG_TYPE_SUPERFAN,
|
BUG_TYPE_SUPERFAN,
|
||||||
|
EXPERT_POKEMON_BREEDER,
|
||||||
|
|
||||||
BROCK = 200,
|
BROCK = 200,
|
||||||
MISTY,
|
MISTY,
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
export enum VariantTier {
|
|
||||||
COMMON,
|
|
||||||
RARE,
|
|
||||||
EPIC
|
|
||||||
}
|
|
|
@ -1,19 +1,19 @@
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
|
import { biomePokemonPools, BiomePoolTier, BiomeTierTrainerPools, biomeTrainerPools, PokemonPools } from "../data/biomes";
|
||||||
import { Constructor } from "#app/utils";
|
import { Constructor } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
||||||
import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather";
|
import { getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage, Weather, WeatherType } from "../data/weather";
|
||||||
import { CommonAnim } from "../data/battle-anims";
|
import { CommonAnim } from "../data/battle-anims";
|
||||||
import { Type } from "../data/type";
|
import { Type } from "../data/type";
|
||||||
import Move from "../data/move";
|
import Move from "../data/move";
|
||||||
import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "../data/arena-tag";
|
import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "../data/arena-tag";
|
||||||
import { BattlerIndex } from "../battle";
|
import { BattlerIndex } from "../battle";
|
||||||
import { Terrain, TerrainType } from "../data/terrain";
|
import { Terrain, TerrainType } from "../data/terrain";
|
||||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
import { applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs, PostTerrainChangeAbAttr, PostWeatherChangeAbAttr } from "../data/ability";
|
||||||
import Pokemon from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "../events/arena";
|
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "../events/arena";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
@ -762,7 +762,7 @@ export class Arena {
|
||||||
case Biome.BEACH:
|
case Biome.BEACH:
|
||||||
return 3.462;
|
return 3.462;
|
||||||
case Biome.LAKE:
|
case Biome.LAKE:
|
||||||
return 5.350;
|
return 7.215;
|
||||||
case Biome.SEABED:
|
case Biome.SEABED:
|
||||||
return 2.600;
|
return 2.600;
|
||||||
case Biome.MOUNTAIN:
|
case Biome.MOUNTAIN:
|
||||||
|
@ -788,7 +788,7 @@ export class Arena {
|
||||||
case Biome.FACTORY:
|
case Biome.FACTORY:
|
||||||
return 4.985;
|
return 4.985;
|
||||||
case Biome.RUINS:
|
case Biome.RUINS:
|
||||||
return 2.270;
|
return 0.000;
|
||||||
case Biome.WASTELAND:
|
case Biome.WASTELAND:
|
||||||
return 6.336;
|
return 6.336;
|
||||||
case Biome.ABYSS:
|
case Biome.ABYSS:
|
||||||
|
|
|
@ -1273,13 +1273,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
|
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
|
||||||
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
|
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
|
||||||
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
|
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
|
||||||
* @returns {AbAttr[]} A list of all the ability attributes on this ability.
|
* @returns A list of all the ability attributes on this ability.
|
||||||
*/
|
*/
|
||||||
getAbilityAttrs(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): AbAttr[] {
|
getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] {
|
||||||
const abilityAttrs: AbAttr[] = [];
|
const abilityAttrs: T[] = [];
|
||||||
|
|
||||||
if (!canApply || this.canApplyAbility()) {
|
if (!canApply || this.canApplyAbility()) {
|
||||||
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType));
|
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs<T>(attrType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canApply || this.canApplyAbility(true)) {
|
if (!canApply || this.canApplyAbility(true)) {
|
||||||
|
@ -2322,11 +2322,61 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return accuracyMultiplier.value / evasionMultiplier.value;
|
return accuracyMultiplier.value / evasionMultiplier.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the base damage of the given move against this Pokemon when attacked by the given source.
|
||||||
|
* Used during damage calculation and for Shell Side Arm's forecasting effect.
|
||||||
|
* @param source the attacking {@linkcode Pokemon}.
|
||||||
|
* @param move the {@linkcode Move} used in the attack.
|
||||||
|
* @param moveCategory the move's {@linkcode MoveCategory} after variable-category effects are applied.
|
||||||
|
* @param ignoreAbility if `true`, ignores this Pokemon's defensive ability effects (defaults to `false`).
|
||||||
|
* @param ignoreSourceAbility if `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`).
|
||||||
|
* @param isCritical if `true`, calculates effective stats as if the hit were critical (defaults to `false`).
|
||||||
|
* @param simulated if `true`, suppresses changes to game state during calculation (defaults to `true`).
|
||||||
|
* @returns The move's base damage against this Pokemon when used by the source Pokemon.
|
||||||
|
*/
|
||||||
|
getBaseDamage(source: Pokemon, move: Move, moveCategory: MoveCategory, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): number {
|
||||||
|
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||||
|
|
||||||
|
/** A base damage multiplier based on the source's level */
|
||||||
|
const levelMultiplier = (2 * source.level / 5 + 2);
|
||||||
|
|
||||||
|
/** The power of the move after power boosts from abilities, etc. have applied */
|
||||||
|
const power = move.calculateBattlePower(source, this, simulated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attacker's offensive stat for the given move's category.
|
||||||
|
* Critical hits cause negative stat stages to be ignored.
|
||||||
|
*/
|
||||||
|
const sourceAtk = new Utils.NumberHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, ignoreSourceAbility, ignoreAbility, isCritical, simulated));
|
||||||
|
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Pokemon's defensive stat for the given move's category.
|
||||||
|
* Critical hits cause positive stat stages to be ignored.
|
||||||
|
*/
|
||||||
|
const targetDef = new Utils.NumberHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, ignoreAbility, ignoreSourceAbility, isCritical, simulated));
|
||||||
|
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attack's base damage, as determined by the source's level, move power
|
||||||
|
* and Attack stat as well as this Pokemon's Defense stat
|
||||||
|
*/
|
||||||
|
const baseDamage = ((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2;
|
||||||
|
|
||||||
|
/** Debug message for non-simulated calls (i.e. when damage is actually dealt) */
|
||||||
|
if (!simulated) {
|
||||||
|
console.log("base damage", baseDamage, move.name, power, sourceAtk.value, targetDef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseDamage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the damage of an attack made by another Pokemon against this Pokemon
|
* Calculates the damage of an attack made by another Pokemon against this Pokemon
|
||||||
* @param source {@linkcode Pokemon} the attacking Pokemon
|
* @param source {@linkcode Pokemon} the attacking Pokemon
|
||||||
* @param move {@linkcode Pokemon} the move used in the attack
|
* @param move {@linkcode Pokemon} the move used in the attack
|
||||||
* @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects
|
* @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects
|
||||||
|
* @param ignoreSourceAbility If `true`, ignores the attacking Pokemon's ability effects
|
||||||
* @param isCritical If `true`, calculates damage for a critical hit.
|
* @param isCritical If `true`, calculates damage for a critical hit.
|
||||||
* @param simulated If `true`, suppresses changes to game state during the calculation.
|
* @param simulated If `true`, suppresses changes to game state during the calculation.
|
||||||
* @returns a {@linkcode DamageCalculationResult} object with three fields:
|
* @returns a {@linkcode DamageCalculationResult} object with three fields:
|
||||||
|
@ -2395,35 +2445,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- BEGIN BASE DAMAGE MULTIPLIERS -----
|
|
||||||
|
|
||||||
/** A base damage multiplier based on the source's level */
|
|
||||||
const levelMultiplier = (2 * source.level / 5 + 2);
|
|
||||||
|
|
||||||
/** The power of the move after power boosts from abilities, etc. have applied */
|
|
||||||
const power = move.calculateBattlePower(source, this, simulated);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attacker's offensive stat for the given move's category.
|
|
||||||
* Critical hits ignore negative stat stages.
|
|
||||||
*/
|
|
||||||
const sourceAtk = new Utils.NumberHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, ignoreSourceAbility, ignoreAbility, isCritical, simulated));
|
|
||||||
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This Pokemon's defensive stat for the given move's category.
|
|
||||||
* Critical hits ignore positive stat stages.
|
|
||||||
*/
|
|
||||||
const targetDef = new Utils.NumberHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, ignoreAbility, ignoreSourceAbility, isCritical, simulated));
|
|
||||||
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attack's base damage, as determined by the source's level, move power
|
* The attack's base damage, as determined by the source's level, move power
|
||||||
* and Attack stat as well as this Pokemon's Defense stat
|
* and Attack stat as well as this Pokemon's Defense stat
|
||||||
*/
|
*/
|
||||||
const baseDamage = ((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2;
|
const baseDamage = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical, simulated);
|
||||||
|
|
||||||
// ------ END BASE DAMAGE MULTIPLIERS ------
|
|
||||||
|
|
||||||
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
|
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
|
||||||
const { targets, multiple } = getMoveTargets(source, move.id);
|
const { targets, multiple } = getMoveTargets(source, move.id);
|
||||||
|
@ -2549,7 +2575,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
// debug message for when damage is applied (i.e. not simulated)
|
// debug message for when damage is applied (i.e. not simulated)
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
console.log("damage", damage.value, move.name, power, sourceAtk, targetDef);
|
console.log("damage", damage.value, move.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hitResult: HitResult;
|
let hitResult: HitResult;
|
||||||
|
|
|
@ -3121,11 +3121,11 @@
|
||||||
},
|
},
|
||||||
"behemothBlade": {
|
"behemothBlade": {
|
||||||
"name": "Gigantenhieb",
|
"name": "Gigantenhieb",
|
||||||
"effect": "Der Anwender wird zu einem riesigen Schwert und greift das Ziel an. Dynamaximierte Ziele erleiden doppelten Schaden."
|
"effect": "Der Anwender wird zu einem riesigen Schwert und greift das Ziel an."
|
||||||
},
|
},
|
||||||
"behemothBash": {
|
"behemothBash": {
|
||||||
"name": "Gigantenstoß",
|
"name": "Gigantenstoß",
|
||||||
"effect": "Der Anwender wird zu einem riesigen Schild und greift das Ziel an. Dynamaximierte Ziele erleiden doppelten Schaden."
|
"effect": "Der Anwender wird zu einem riesigen Schild und greift das Ziel an."
|
||||||
},
|
},
|
||||||
"auraWheel": {
|
"auraWheel": {
|
||||||
"name": "Aura-Rad",
|
"name": "Aura-Rad",
|
||||||
|
|
|
@ -283,5 +283,9 @@
|
||||||
"INVERSE_BATTLE": {
|
"INVERSE_BATTLE": {
|
||||||
"name": "Mirror rorriM",
|
"name": "Mirror rorriM",
|
||||||
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||||
|
},
|
||||||
|
"BREEDERS_IN_SPACE": {
|
||||||
|
"name": "Breeders in Space!",
|
||||||
|
"description": "Beat the Expert Pokémon Breeder in the Space Biome."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,13 +112,13 @@
|
||||||
"island": "PMD EoS Craggy Coast",
|
"island": "PMD EoS Craggy Coast",
|
||||||
"jungle": "Lmz - Jungle",
|
"jungle": "Lmz - Jungle",
|
||||||
"laboratory": "Firel - Laboratory",
|
"laboratory": "Firel - Laboratory",
|
||||||
"lake": "PMD EoS Crystal Cave",
|
"lake": "Lmz - Lake",
|
||||||
"meadow": "PMD EoS Sky Peak Forest",
|
"meadow": "PMD EoS Sky Peak Forest",
|
||||||
"metropolis": "Firel - Metropolis",
|
"metropolis": "Firel - Metropolis",
|
||||||
"mountain": "PMD EoS Mt. Horn",
|
"mountain": "PMD EoS Mt. Horn",
|
||||||
"plains": "Firel - Route 888",
|
"plains": "Firel - Route 888",
|
||||||
"power_plant": "Firel - The Klink",
|
"power_plant": "Firel - The Klink",
|
||||||
"ruins": "PMD EoS Deep Sealed Ruin",
|
"ruins": "Lmz - Ancient Ruins",
|
||||||
"sea": "Andr06 - Marine Mystique",
|
"sea": "Andr06 - Marine Mystique",
|
||||||
"seabed": "Firel - Seabed",
|
"seabed": "Firel - Seabed",
|
||||||
"slum": "Andr06 - Sneaky Snom",
|
"slum": "Andr06 - Sneaky Snom",
|
||||||
|
@ -151,5 +151,6 @@
|
||||||
"mystery_encounter_weird_dream": "PMD EoS Temporal Spire",
|
"mystery_encounter_weird_dream": "PMD EoS Temporal Spire",
|
||||||
"mystery_encounter_fun_and_games": "PMD EoS Guildmaster Wigglytuff",
|
"mystery_encounter_fun_and_games": "PMD EoS Guildmaster Wigglytuff",
|
||||||
"mystery_encounter_gen_5_gts": "BW GTS",
|
"mystery_encounter_gen_5_gts": "BW GTS",
|
||||||
"mystery_encounter_gen_6_gts": "XY GTS"
|
"mystery_encounter_gen_6_gts": "XY GTS",
|
||||||
|
"mystery_encounter_delibirdy": "Firel - DeliDelivery!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ import bugTypeSuperfan from "#app/locales/en/mystery-encounters/bug-type-superfa
|
||||||
import funAndGames from "#app/locales/en/mystery-encounters/fun-and-games-dialogue.json";
|
import funAndGames from "#app/locales/en/mystery-encounters/fun-and-games-dialogue.json";
|
||||||
import uncommonBreed from "#app/locales/en/mystery-encounters/uncommon-breed-dialogue.json";
|
import uncommonBreed from "#app/locales/en/mystery-encounters/uncommon-breed-dialogue.json";
|
||||||
import globalTradeSystem from "#app/locales/en/mystery-encounters/global-trade-system-dialogue.json";
|
import globalTradeSystem from "#app/locales/en/mystery-encounters/global-trade-system-dialogue.json";
|
||||||
|
import expertPokemonBreeder from "#app/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialogue/Text token injection patterns that can be used:
|
* Dialogue/Text token injection patterns that can be used:
|
||||||
|
@ -183,7 +184,8 @@ export const enConfig = {
|
||||||
bugTypeSuperfan,
|
bugTypeSuperfan,
|
||||||
funAndGames,
|
funAndGames,
|
||||||
uncommonBreed,
|
uncommonBreed,
|
||||||
globalTradeSystem
|
globalTradeSystem,
|
||||||
|
expertPokemonBreeder
|
||||||
},
|
},
|
||||||
mysteryEncounterMessages
|
mysteryEncounterMessages
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"intro": "You're stopped by a rich looking boy.",
|
"intro": "You're stopped by a rich looking boy.",
|
||||||
"speaker": "Rich Boy",
|
"speaker": "Rich Boy",
|
||||||
"intro_dialogue": "Good day to you.$I can't help but notice that your\n{{strongestPokemon}} looks positively divine!$I've always wanted to have a pet like that!$I'd pay you handsomely,\nand also give you this old bauble!",
|
"intro_dialogue": "Good day to you.$I can't help but notice that your\n{{strongestPokemon}} looks positively divine!$I've always wanted to have a Pokémon like that!$I'd pay you handsomely,\nand also give you this old bauble!",
|
||||||
"title": "An Offer You Can't Refuse",
|
"title": "An Offer You Can't Refuse",
|
||||||
"description": "You're being offered a @[TOOLTIP_TITLE]{Shiny Charm} and {{price, money}} for your {{strongestPokemon}}!\n\nIt's an extremely good deal, but can you really bear to part with such a strong team member?",
|
"description": "You're being offered a @[TOOLTIP_TITLE]{Shiny Charm} and {{price, money}} for your {{strongestPokemon}}!\n\nIt's an extremely good deal, but can you really bear to part with such a strong team member?",
|
||||||
"query": "What will you do?",
|
"query": "What will you do?",
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
"disabled_tooltip": "You need at least 1 Bug Type Pokémon on your team to select this.",
|
"disabled_tooltip": "You need at least 1 Bug Type Pokémon on your team to select this.",
|
||||||
"selected": "You show the trainer all your Bug Type Pokémon...",
|
"selected": "You show the trainer all your Bug Type Pokémon...",
|
||||||
"selected_0_to_1": "Huh? You only have {{numBugTypes}}...$Guess I'm wasting my breath on someone like you...",
|
"selected_0_to_1": "Huh? You only have {{numBugTypes}}...$Guess I'm wasting my breath on someone like you...",
|
||||||
"selected_2_to_3": "Hey, you've got {{numBugTypes}} Bug Types!\nNot bad.$Here, this might help you on your journey to catch more!",
|
"selected_2_to_3": "Hey, you've got {{numBugTypes}}!\nNot bad.$Here, this might help you on your journey to catch more!",
|
||||||
"selected_4_to_5": "What? You have {{numBugTypes}} Bug Types?\nNice!$You're not quite at my level, but I can see shades of myself in you!\n$Take this, my young apprentice!",
|
"selected_4_to_5": "What? You have {{numBugTypes}}?\nNice!$You're not quite at my level, but I can see shades of myself in you!\n$Take this, my young apprentice!",
|
||||||
"selected_6": "Whoa! {{numBugTypes}} Bug Types!\n$You must love Bug Types almost as much as I do!$Here, take this as a token of our camaraderie!"
|
"selected_6": "Whoa! {{numBugTypes}}!\n$You must love Bug Types almost as much as I do!$Here, take this as a token of our camaraderie!"
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"label": "Gift a Bug Item",
|
"label": "Gift a Bug Item",
|
||||||
|
@ -34,5 +34,7 @@
|
||||||
"battle_won": "Your knowledge and skill were perfect at exploiting our weaknesses!$In exchange for the valuable lesson,\nallow me to teach one of your Pokémon a Bug Type Move!",
|
"battle_won": "Your knowledge and skill were perfect at exploiting our weaknesses!$In exchange for the valuable lesson,\nallow me to teach one of your Pokémon a Bug Type Move!",
|
||||||
"teach_move_prompt": "Select a move to teach a Pokémon.",
|
"teach_move_prompt": "Select a move to teach a Pokémon.",
|
||||||
"confirm_no_teach": "You sure you don't want to learn one of these great moves?",
|
"confirm_no_teach": "You sure you don't want to learn one of these great moves?",
|
||||||
"outro": "I see great Bug Pokémon in your future!\nMay our paths cross again!$Bug out!"
|
"outro": "I see great Bug Pokémon in your future!\nMay our paths cross again!$Bug out!",
|
||||||
|
"numBugTypes_one": "{{count}} Bug Type",
|
||||||
|
"numBugTypes_other": "{{count}} Bug Types"
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@
|
||||||
"label": "Sales Assistant",
|
"label": "Sales Assistant",
|
||||||
"tooltip": "(-) Your {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Earn @[MONEY]{Money}",
|
"tooltip": "(-) Your {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Earn @[MONEY]{Money}",
|
||||||
"disabled_tooltip": "Your Pokémon need to know certain moves for this job",
|
"disabled_tooltip": "Your Pokémon need to know certain moves for this job",
|
||||||
"selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to attract customers to the business!"
|
"selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to draw customers to the business!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"job_complete_good": "Thanks for the assistance!\nYour {{selectedPokemon}} was incredibly helpful!$Here's your check for the day.",
|
"job_complete_good": "Thanks for the assistance!\nYour {{selectedPokemon}} was incredibly helpful!$Here's your check for the day.",
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"intro": "It's a trainer carrying tons of Pokémon Eggs!",
|
||||||
|
"intro_dialogue": "Hey there, trainer!$It looks like some of your\npartner Pokémon are feeling a little down.$Why not have a battle with me to cheer them up?",
|
||||||
|
"title": "The Expert Pokémon Breeder",
|
||||||
|
"description": "You've been challenged to a battle where @[TOOLTIP_TITLE]{you can only use a single Pokémon}. It might be tough, but it would surely deepen the bond you have with the Pokémon you choose!\nThe breeder will also give you some @[TOOLTIP_TITLE]{Pokémon Eggs} if you win!",
|
||||||
|
"query": "Who will you battle with?",
|
||||||
|
"cleffa_1_nickname": "Ace",
|
||||||
|
"cleffa_2_nickname": "Clefablest",
|
||||||
|
"cleffa_3_nickname": "{{speciesName}} the Great",
|
||||||
|
"option": {
|
||||||
|
"1": {
|
||||||
|
"label": "{{pokemon1Name}}",
|
||||||
|
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon1Name}}"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"label": "{{pokemon2Name}}",
|
||||||
|
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon2Name}}"
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"label": "{{pokemon3Name}}",
|
||||||
|
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon3Name}}"
|
||||||
|
},
|
||||||
|
"selected": "Let's do this!"
|
||||||
|
},
|
||||||
|
"outro": "Look how happy your {{chosenPokemon}} is now!$Here, you can have these as well.",
|
||||||
|
"gained_eggs": "@s{item_fanfare}You received {{numEggs}}!",
|
||||||
|
"eggs_tooltip": "\n(+) Earn {{eggs}}",
|
||||||
|
"numEggs_one": "{{count}} {{rarity}} Egg",
|
||||||
|
"numEggs_other": "{{count}} {{rarity}} Eggs"
|
||||||
|
}
|
|
@ -172,5 +172,6 @@
|
||||||
"vivi": "Vivi",
|
"vivi": "Vivi",
|
||||||
"vicky": "Vicky",
|
"vicky": "Vicky",
|
||||||
"vito": "Vito",
|
"vito": "Vito",
|
||||||
"bug_type_superfan": "Bug-Type Superfan"
|
"bug_type_superfan": "Bug-Type Superfan",
|
||||||
|
"expert_pokemon_breeder": "Expert Pokémon Breeder"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3121,11 +3121,11 @@
|
||||||
},
|
},
|
||||||
"behemothBlade": {
|
"behemothBlade": {
|
||||||
"name": "Gladius Maximus",
|
"name": "Gladius Maximus",
|
||||||
"effect": "Le lanceur se transforme en une immense épée et pourfend sa cible. Cette capacité inflige le double de dégâts aux Pokémon Dynamax."
|
"effect": "Le lanceur se transforme en une immense épée et pourfend sa cible."
|
||||||
},
|
},
|
||||||
"behemothBash": {
|
"behemothBash": {
|
||||||
"name": "Aegis Maxima",
|
"name": "Aegis Maxima",
|
||||||
"effect": "Le lanceur se transforme en un immense bouclier et charge sa cible. Cette capacité inflige le double de dégâts aux Pokémon Dynamax."
|
"effect": "Le lanceur se transforme en un immense bouclier et charge sa cible."
|
||||||
},
|
},
|
||||||
"auraWheel": {
|
"auraWheel": {
|
||||||
"name": "Roue Libre",
|
"name": "Roue Libre",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"ALL": "Tout",
|
"ALL": "Tout",
|
||||||
"PASS_BATON": "Relais",
|
"PASS_BATON": "Relais",
|
||||||
"UNPAUSE_EVOLUTION": "Réactiver Évolution",
|
"UNPAUSE_EVOLUTION": "Réactiver Évolution",
|
||||||
"PAUSE_EVOLUTION": "Interrompre Évolution",
|
"PAUSE_EVOLUTION": "Empêcher Évolution",
|
||||||
"REVIVE": "Ranimer",
|
"REVIVE": "Ranimer",
|
||||||
"RENAME": "Renommer",
|
"RENAME": "Renommer",
|
||||||
"choosePokemon": "Sélectionnez un Pokémon.",
|
"choosePokemon": "Sélectionnez un Pokémon.",
|
||||||
|
|
|
@ -600,7 +600,7 @@ export class TerastallizeAccessModifier extends PersistentModifier {
|
||||||
|
|
||||||
export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
||||||
public pokemonId: integer;
|
public pokemonId: integer;
|
||||||
readonly isTransferrable: boolean = true;
|
public isTransferable: boolean = true;
|
||||||
|
|
||||||
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
super(type, stackCount);
|
super(type, stackCount);
|
||||||
|
@ -699,7 +699,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
||||||
|
|
||||||
export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier {
|
export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier {
|
||||||
protected battlesLeft: integer;
|
protected battlesLeft: integer;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) {
|
constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -736,7 +736,7 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi
|
||||||
|
|
||||||
export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
|
export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
|
||||||
public teraType: Type;
|
public teraType: Type;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierTypes.TerastallizeModifierType, pokemonId: integer, teraType: Type, battlesLeft?: integer, stackCount?: integer) {
|
constructor(type: ModifierTypes.TerastallizeModifierType, pokemonId: integer, teraType: Type, battlesLeft?: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, battlesLeft || 10, stackCount);
|
super(type, pokemonId, battlesLeft || 10, stackCount);
|
||||||
|
@ -799,7 +799,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
|
||||||
*/
|
*/
|
||||||
export class BaseStatModifier extends PokemonHeldItemModifier {
|
export class BaseStatModifier extends PokemonHeldItemModifier {
|
||||||
protected stat: PermanentStat;
|
protected stat: PermanentStat;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierType, pokemonId: integer, stat: PermanentStat, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, stat: PermanentStat, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -843,7 +843,7 @@ export class BaseStatModifier extends PokemonHeldItemModifier {
|
||||||
export class EvoTrackerModifier extends PokemonHeldItemModifier {
|
export class EvoTrackerModifier extends PokemonHeldItemModifier {
|
||||||
protected species: Species;
|
protected species: Species;
|
||||||
protected required: integer;
|
protected required: integer;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierType, pokemonId: integer, species: Species, required: integer, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, species: Species, required: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -880,7 +880,7 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier {
|
||||||
*/
|
*/
|
||||||
export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier {
|
export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier {
|
||||||
private statModifier: integer;
|
private statModifier: integer;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) {
|
constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -929,7 +929,7 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier {
|
||||||
export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier {
|
export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier {
|
||||||
private statModifier: integer;
|
private statModifier: integer;
|
||||||
private stats: Stat[];
|
private stats: Stat[];
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor (type: ModifierType, pokemonId: integer, statModifier: integer, stats: Stat[], stackCount?: integer) {
|
constructor (type: ModifierType, pokemonId: integer, statModifier: integer, stats: Stat[], stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -979,7 +979,7 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier {
|
||||||
* Currently used by Macho Brace item
|
* Currently used by Macho Brace item
|
||||||
*/
|
*/
|
||||||
export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier {
|
export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier {
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor (type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
constructor (type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -2346,7 +2346,7 @@ export class PokemonMultiHitModifier extends PokemonHeldItemModifier {
|
||||||
export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
|
export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
|
||||||
public formChangeItem: FormChangeItem;
|
public formChangeItem: FormChangeItem;
|
||||||
public active: boolean;
|
public active: boolean;
|
||||||
readonly isTransferrable: boolean = false;
|
public isTransferable: boolean = false;
|
||||||
|
|
||||||
constructor(type: ModifierTypes.FormChangeItemModifierType, pokemonId: integer, formChangeItem: FormChangeItem, active: boolean, stackCount?: integer) {
|
constructor(type: ModifierTypes.FormChangeItemModifierType, pokemonId: integer, formChangeItem: FormChangeItem, active: boolean, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -2691,7 +2691,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
||||||
|
|
||||||
const transferredModifierTypes: ModifierTypes.ModifierType[] = [];
|
const transferredModifierTypes: ModifierTypes.ModifierType[] = [];
|
||||||
const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
&& m.pokemonId === targetPokemon.id && m.isTransferrable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[];
|
&& m.pokemonId === targetPokemon.id && m.isTransferable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[];
|
||||||
let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct?
|
let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct?
|
||||||
let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
||||||
|
|
||||||
|
@ -2736,7 +2736,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
||||||
* @see {@linkcode modifierTypes[MINI_BLACK_HOLE]}
|
* @see {@linkcode modifierTypes[MINI_BLACK_HOLE]}
|
||||||
*/
|
*/
|
||||||
export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
|
export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
|
||||||
isTransferrable: boolean = true;
|
isTransferable: boolean = true;
|
||||||
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
}
|
}
|
||||||
|
@ -2762,7 +2762,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
setTransferrableFalse(): void {
|
setTransferrableFalse(): void {
|
||||||
this.isTransferrable = false;
|
this.isTransferable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { PokeballType } from "#enums/pokeball";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import { VariantTier } from "#enums/variant-tiers";
|
import { VariantTier } from "#enums/variant-tier";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { type PokeballCounts } from "./battle-scene";
|
import { type PokeballCounts } from "./battle-scene";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
|
|
|
@ -16,6 +16,7 @@ import i18next from "i18next";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
import { SelectTargetPhase } from "./select-target-phase";
|
import { SelectTargetPhase } from "./select-target-phase";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
|
||||||
export class CommandPhase extends FieldPhase {
|
export class CommandPhase extends FieldPhase {
|
||||||
protected fieldIndex: integer;
|
protected fieldIndex: integer;
|
||||||
|
@ -179,14 +180,16 @@ export class CommandPhase extends FieldPhase {
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
case Command.RUN:
|
case Command.RUN:
|
||||||
const isSwitch = command === Command.POKEMON;
|
const isSwitch = command === Command.POKEMON;
|
||||||
if (!isSwitch && this.scene.arena.biomeType === Biome.END) {
|
const { currentBattle, arena } = this.scene;
|
||||||
|
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
|
||||||
|
if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) {
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => {
|
this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => {
|
||||||
this.scene.ui.showText("", 0);
|
this.scene.ui.showText("", 0);
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}, null, true);
|
}, null, true);
|
||||||
} else if (!isSwitch && (this.scene.currentBattle.battleType === BattleType.TRAINER || this.scene.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) {
|
} else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) {
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => {
|
this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => {
|
||||||
|
@ -197,12 +200,12 @@ export class CommandPhase extends FieldPhase {
|
||||||
const batonPass = isSwitch && args[0] as boolean;
|
const batonPass = isSwitch && args[0] as boolean;
|
||||||
const trappedAbMessages: string[] = [];
|
const trappedAbMessages: string[] = [];
|
||||||
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) {
|
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||||
: { command: Command.RUN };
|
: { command: Command.RUN };
|
||||||
success = true;
|
success = true;
|
||||||
if (!isSwitch && this.fieldIndex) {
|
if (!isSwitch && this.fieldIndex) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
||||||
}
|
}
|
||||||
} else if (trappedAbMessages.length > 0) {
|
} else if (trappedAbMessages.length > 0) {
|
||||||
if (!isSwitch) {
|
if (!isSwitch) {
|
||||||
|
@ -219,7 +222,7 @@ export class CommandPhase extends FieldPhase {
|
||||||
|
|
||||||
// trapTag should be defined at this point, but just in case...
|
// trapTag should be defined at this point, but just in case...
|
||||||
if (!trapTag) {
|
if (!trapTag) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||||
: { command: Command.RUN };
|
: { command: Command.RUN };
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -60,6 +60,11 @@ export class GameOverPhase extends BattlePhase {
|
||||||
this.scene.ui.fadeOut(1250).then(() => {
|
this.scene.ui.fadeOut(1250).then(() => {
|
||||||
this.scene.reset();
|
this.scene.reset();
|
||||||
this.scene.clearPhaseQueue();
|
this.scene.clearPhaseQueue();
|
||||||
|
// If this is a ME, clear any residual visual sprites before reloading
|
||||||
|
const encounter = this.scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter?.introVisuals) {
|
||||||
|
this.scene.field.remove(encounter.introVisuals, true);
|
||||||
|
}
|
||||||
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
||||||
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
||||||
|
|
||||||
|
@ -238,7 +243,7 @@ export class GameOverPhase extends BattlePhase {
|
||||||
gameVersion: this.scene.game.config.gameVersion,
|
gameVersion: this.scene.game.config.gameVersion,
|
||||||
timestamp: new Date().getTime(),
|
timestamp: new Date().getTime(),
|
||||||
challenges: this.scene.gameMode.challenges.map(c => new ChallengeData(c)),
|
challenges: this.scene.gameMode.challenges.map(c => new ChallengeData(c)),
|
||||||
mysteryEncounterType: this.scene.currentBattle.mysteryEncounter?.encounterType,
|
mysteryEncounterType: this.scene.currentBattle.mysteryEncounter?.encounterType ?? -1,
|
||||||
mysteryEncounterSaveData: this.scene.mysteryEncounterSaveData
|
mysteryEncounterSaveData: this.scene.mysteryEncounterSaveData
|
||||||
} as SessionSaveData;
|
} as SessionSaveData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,16 @@ export class MessagePhase extends Phase {
|
||||||
private callbackDelay: integer | null;
|
private callbackDelay: integer | null;
|
||||||
private prompt: boolean | null;
|
private prompt: boolean | null;
|
||||||
private promptDelay: integer | null;
|
private promptDelay: integer | null;
|
||||||
|
private speaker?: string;
|
||||||
|
|
||||||
constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) {
|
constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, speaker?: string) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.callbackDelay = callbackDelay!; // TODO: is this bang correct?
|
this.callbackDelay = callbackDelay!; // TODO: is this bang correct?
|
||||||
this.prompt = prompt!; // TODO: is this bang correct?
|
this.prompt = prompt!; // TODO: is this bang correct?
|
||||||
this.promptDelay = promptDelay!; // TODO: is this bang correct?
|
this.promptDelay = promptDelay!; // TODO: is this bang correct?
|
||||||
|
this.speaker = speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
@ -21,12 +23,16 @@ export class MessagePhase extends Phase {
|
||||||
|
|
||||||
if (this.text.indexOf("$") > -1) {
|
if (this.text.indexOf("$") > -1) {
|
||||||
const pageIndex = this.text.indexOf("$");
|
const pageIndex = this.text.indexOf("$");
|
||||||
this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay));
|
this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay, this.speaker));
|
||||||
this.text = this.text.slice(0, pageIndex).trim();
|
this.text = this.text.slice(0, pageIndex).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.speaker) {
|
||||||
|
this.scene.ui.showDialogue(this.text, this.speaker, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.promptDelay ?? 0);
|
||||||
|
} else {
|
||||||
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay);
|
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
end() {
|
end() {
|
||||||
if (this.scene.abilityBar.shown) {
|
if (this.scene.abilityBar.shown) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import BattleScene from "../battle-scene";
|
||||||
import { Phase } from "../phase";
|
import { Phase } from "../phase";
|
||||||
import { Mode } from "../ui/ui";
|
import { Mode } from "../ui/ui";
|
||||||
import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { getCharVariantFromDialogue } from "../data/dialogue";
|
import { getCharVariantFromDialogue } from "../data/dialogue";
|
||||||
import { TrainerSlot } from "../data/trainer-config";
|
import { TrainerSlot } from "../data/trainer-config";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type";
|
import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type";
|
||||||
import { ExtraModifierModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { ExtraModifierModifier, HealShopCostModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
|
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
|
||||||
import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler";
|
import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
|
@ -10,7 +10,7 @@ import * as Utils from "#app/utils";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { CustomModifierSettings } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined, NumberHolder } from "#app/utils";
|
||||||
|
|
||||||
export class SelectModifierPhase extends BattlePhase {
|
export class SelectModifierPhase extends BattlePhase {
|
||||||
private rerollCount: integer;
|
private rerollCount: integer;
|
||||||
|
@ -69,11 +69,11 @@ export class SelectModifierPhase extends BattlePhase {
|
||||||
}
|
}
|
||||||
let modifierType: ModifierType;
|
let modifierType: ModifierType;
|
||||||
let cost: integer;
|
let cost: integer;
|
||||||
|
const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers);
|
||||||
switch (rowCursor) {
|
switch (rowCursor) {
|
||||||
case 0:
|
case 0:
|
||||||
switch (cursor) {
|
switch (cursor) {
|
||||||
case 0:
|
case 0:
|
||||||
const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers);
|
|
||||||
if (rerollCost < 0 || this.scene.money < rerollCost) {
|
if (rerollCost < 0 || this.scene.money < rerollCost) {
|
||||||
this.scene.ui.playError();
|
this.scene.ui.playError();
|
||||||
return false;
|
return false;
|
||||||
|
@ -94,7 +94,7 @@ export class SelectModifierPhase extends BattlePhase {
|
||||||
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => {
|
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => {
|
||||||
if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) {
|
if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) {
|
||||||
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
&& m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[];
|
&& m.isTransferable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[];
|
||||||
const itemModifier = itemModifiers[itemIndex];
|
const itemModifier = itemModifiers[itemIndex];
|
||||||
this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity);
|
this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity);
|
||||||
} else {
|
} else {
|
||||||
|
@ -108,6 +108,11 @@ export class SelectModifierPhase extends BattlePhase {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
if (rerollCost < 0) {
|
||||||
|
// Reroll lock button is also disabled when reroll is disabled
|
||||||
|
this.scene.ui.playError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
this.scene.lockModifierTiers = !this.scene.lockModifierTiers;
|
this.scene.lockModifierTiers = !this.scene.lockModifierTiers;
|
||||||
const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
|
uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
|
||||||
|
@ -133,7 +138,10 @@ export class SelectModifierPhase extends BattlePhase {
|
||||||
if (shopOption.type) {
|
if (shopOption.type) {
|
||||||
modifierType = shopOption.type;
|
modifierType = shopOption.type;
|
||||||
}
|
}
|
||||||
cost = shopOption.cost;
|
// Apply Black Sludge to healing item cost
|
||||||
|
const healingItemCost = new NumberHolder(shopOption.cost);
|
||||||
|
this.scene.applyModifier(HealShopCostModifier, true, healingItemCost);
|
||||||
|
cost = healingItemCost.value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
import * as Utils from "#app/utils";
|
import { NumberHolder, BooleanHolder } from "#app/utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
||||||
|
@ -42,17 +42,23 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||||
return this.end();
|
return this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stages = new NumberHolder(this.stages);
|
||||||
|
|
||||||
|
if (!this.ignoreAbilities) {
|
||||||
|
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages);
|
||||||
|
}
|
||||||
|
|
||||||
let simulate = false;
|
let simulate = false;
|
||||||
|
|
||||||
const filteredStats = this.stats.filter(stat => {
|
const filteredStats = this.stats.filter(stat => {
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (!this.selfTarget && this.stages < 0) {
|
if (!this.selfTarget && stages.value < 0) {
|
||||||
// TODO: Include simulate boolean when tag applications can be simulated
|
// TODO: Include simulate boolean when tag applications can be simulated
|
||||||
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cancelled.value && !this.selfTarget && this.stages < 0) {
|
if (!cancelled.value && !this.selfTarget && stages.value < 0) {
|
||||||
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate);
|
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,12 +70,6 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const stages = new Utils.IntegerHolder(this.stages);
|
|
||||||
|
|
||||||
if (!this.ignoreAbilities) {
|
|
||||||
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages);
|
|
||||||
}
|
|
||||||
|
|
||||||
const relLevels = filteredStats.map(s => (stages.value >= 1 ? Math.min(pokemon.getStatStage(s) + stages.value, 6) : Math.max(pokemon.getStatStage(s) + stages.value, -6)) - pokemon.getStatStage(s));
|
const relLevels = filteredStats.map(s => (stages.value >= 1 ? Math.min(pokemon.getStatStage(s) + stages.value, 6) : Math.max(pokemon.getStatStage(s) + stages.value, -6)) - pokemon.getStatStage(s));
|
||||||
|
|
||||||
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
||||||
|
|
|
@ -279,6 +279,8 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||||
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
||||||
case "INVERSE_BATTLE":
|
case "INVERSE_BATTLE":
|
||||||
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
||||||
|
case "BREEDERS_IN_SPACE":
|
||||||
|
return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr });
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -356,6 +358,7 @@ export const achvs = {
|
||||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
||||||
|
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 100).setSecret(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function initAchievements() {
|
export function initAchievements() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { VariantTier } from "#enums/variant-tiers";
|
import { VariantTier } from "#enums/variant-tier";
|
||||||
import { EGG_SEED, Egg } from "../data/egg";
|
import { EGG_SEED, Egg } from "../data/egg";
|
||||||
import { EggSourceType } from "#app/enums/egg-source-types";
|
import { EggSourceType } from "#app/enums/egg-source-types";
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe("Abilities - Contrary", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should invert stat changes when applied", async() => {
|
it("should invert stat changes when applied", async() => {
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.SLOWBRO
|
Species.SLOWBRO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -39,4 +39,39 @@ describe("Abilities - Contrary", () => {
|
||||||
|
|
||||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
|
describe("With Clear Body", () => {
|
||||||
|
it("should apply positive effects", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyPassiveAbility(Abilities.CLEAR_BODY)
|
||||||
|
.moveset([Moves.TAIL_WHIP]);
|
||||||
|
await game.classicMode.startBattle([Species.SLOWBRO]);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.TAIL_WHIP);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should block negative effects", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyPassiveAbility(Abilities.CLEAR_BODY)
|
||||||
|
.enemyMoveset([Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL])
|
||||||
|
.moveset([Moves.SPLASH]);
|
||||||
|
await game.classicMode.startBattle([Species.SLOWBRO]);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg";
|
import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg";
|
||||||
import { EggSourceType } from "#app/enums/egg-source-types";
|
import { EggSourceType } from "#app/enums/egg-source-types";
|
||||||
import { EggTier } from "#app/enums/egg-type";
|
import { EggTier } from "#app/enums/egg-type";
|
||||||
import { VariantTier } from "#app/enums/variant-tiers";
|
import { VariantTier } from "#app/enums/variant-tier";
|
||||||
import EggData from "#app/system/egg-data";
|
import EggData from "#app/system/egg-data";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -136,9 +136,9 @@ describe("Egg Generation Tests", () => {
|
||||||
|
|
||||||
expect(result).toBe(expectedResult);
|
expect(result).toBe(expectedResult);
|
||||||
});
|
});
|
||||||
it("should return a shiny common variant", () => {
|
it("should return a shiny standard variant", () => {
|
||||||
const scene = game.scene;
|
const scene = game.scene;
|
||||||
const expectedVariantTier = VariantTier.COMMON;
|
const expectedVariantTier = VariantTier.STANDARD;
|
||||||
|
|
||||||
const result = new Egg({ scene, isShiny: true, variantTier: expectedVariantTier, species: Species.BULBASAUR }).generatePlayerPokemon(scene).variant;
|
const result = new Egg({ scene, isShiny: true, variantTier: expectedVariantTier, species: Species.BULBASAUR }).generatePlayerPokemon(scene).variant;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves, ShellSideArmCategoryAttr } from "#app/data/move";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Shell Side Arm", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.SHELL_SIDE_ARM])
|
||||||
|
.battleType("single")
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("becomes a physical attack if forecasted to deal more damage as physical", async () => {
|
||||||
|
game.override.enemySpecies(Species.SNORLAX);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.MANAPHY]);
|
||||||
|
|
||||||
|
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
|
||||||
|
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
|
||||||
|
vi.spyOn(shellSideArmAttr, "apply");
|
||||||
|
|
||||||
|
game.move.select(Moves.SHELL_SIDE_ARM);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
|
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("remains a special attack if forecasted to deal more damage as special", async () => {
|
||||||
|
game.override.enemySpecies(Species.SLOWBRO);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.MANAPHY]);
|
||||||
|
|
||||||
|
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
|
||||||
|
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
|
||||||
|
vi.spyOn(shellSideArmAttr, "apply");
|
||||||
|
|
||||||
|
game.move.select(Moves.SHELL_SIDE_ARM);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
|
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("respects stat stage changes when forecasting base damage", async () => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.SNORLAX)
|
||||||
|
.enemyMoveset(Moves.COTTON_GUARD);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.MANAPHY]);
|
||||||
|
|
||||||
|
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
|
||||||
|
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
|
||||||
|
vi.spyOn(shellSideArmAttr, "apply");
|
||||||
|
|
||||||
|
game.move.select(Moves.SHELL_SIDE_ARM);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
|
import { DamageCalculationResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Steamroller", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override.moveset([Moves.STEAMROLLER]).battleType("single").enemyAbility(Abilities.BALL_FETCH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should always hit a minimzed target with double damage", async () => {
|
||||||
|
game.override.enemySpecies(Species.DITTO).enemyMoveset(Moves.MINIMIZE);
|
||||||
|
await game.classicMode.startBattle([Species.IRON_BOULDER]);
|
||||||
|
|
||||||
|
const ditto = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(ditto, "getAttackDamage");
|
||||||
|
ditto.hp = 5000;
|
||||||
|
const steamroller = allMoves[Moves.STEAMROLLER];
|
||||||
|
vi.spyOn(steamroller, "calculateBattleAccuracy");
|
||||||
|
const ironBoulder = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(ironBoulder, "getAccuracyMultiplier");
|
||||||
|
// Turn 1
|
||||||
|
game.move.select(Moves.STEAMROLLER);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
// Turn 2
|
||||||
|
game.move.select(Moves.STEAMROLLER);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const [dmgCalcTurn1, dmgCalcTurn2]: DamageCalculationResult[] = vi
|
||||||
|
.mocked(ditto.getAttackDamage)
|
||||||
|
.mock.results.map((r) => r.value);
|
||||||
|
|
||||||
|
expect(dmgCalcTurn2.damage).toBeGreaterThanOrEqual(dmgCalcTurn1.damage * 2);
|
||||||
|
expect(ditto.getTag(BattlerTagType.MINIMIZED)).toBeDefined();
|
||||||
|
expect(steamroller.calculateBattleAccuracy).toHaveReturnedWith(-1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Whirlwind", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.WHIRLWIND)
|
||||||
|
.enemySpecies(Species.PIDGEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ move: Moves.FLY, name: "Fly" },
|
||||||
|
{ move: Moves.BOUNCE, name: "Bounce" },
|
||||||
|
{ move: Moves.SKY_DROP, name: "Sky Drop" },
|
||||||
|
])("should not hit a flying target: $name (=$move)", async ({ move }) => {
|
||||||
|
game.override.moveset([move]);
|
||||||
|
await game.classicMode.startBattle([Species.STARAPTOR]);
|
||||||
|
|
||||||
|
const staraptor = game.scene.getPlayerPokemon()!;
|
||||||
|
const whirlwind = allMoves[Moves.WHIRLWIND];
|
||||||
|
vi.spyOn(whirlwind, "getFailedText");
|
||||||
|
|
||||||
|
game.move.select(move);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(staraptor.findTag((t) => t.tagType === BattlerTagType.FLYING)).toBeDefined();
|
||||||
|
expect(whirlwind.getFailedText).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
|
@ -69,22 +69,6 @@ describe("A Trainer's Test - Mystery Encounter", () => {
|
||||||
expect(ATrainersTestEncounter.options.length).toBe(2);
|
expect(ATrainersTestEncounter.options.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.A_TRAINERS_TEST);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should initialize fully ", async () => {
|
it("should initialize fully ", async () => {
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = ATrainersTestEncounter;
|
scene.currentBattle.mysteryEncounter = ATrainersTestEncounter;
|
||||||
|
|
|
@ -66,22 +66,6 @@ describe("Absolute Avarice - Mystery Encounter", () => {
|
||||||
expect(AbsoluteAvariceEncounter.options.length).toBe(3);
|
expect(AbsoluteAvariceEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not spawn outside of proper biomes", async () => {
|
it("should not spawn outside of proper biomes", async () => {
|
||||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||||
game.override.startingBiome(Biome.VOLCANO);
|
game.override.startingBiome(Biome.VOLCANO);
|
||||||
|
|
|
@ -80,22 +80,6 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE);
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should initialize fully ", async () => {
|
it("should initialize fully ", async () => {
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = AnOfferYouCantRefuseEncounter;
|
scene.currentBattle.mysteryEncounter = AnOfferYouCantRefuseEncounter;
|
||||||
|
|
|
@ -69,22 +69,6 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||||
expect(BerriesAboundEncounter.options.length).toBe(3);
|
expect(BerriesAboundEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BERRIES_ABOUND);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should initialize fully", async () => {
|
it("should initialize fully", async () => {
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = BerriesAboundEncounter;
|
scene.currentBattle.mysteryEncounter = BerriesAboundEncounter;
|
||||||
|
@ -98,7 +82,6 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||||
|
|
||||||
const config = BerriesAboundEncounter.enemyPartyConfigs[0];
|
const config = BerriesAboundEncounter.enemyPartyConfigs[0];
|
||||||
expect(config).toBeDefined();
|
expect(config).toBeDefined();
|
||||||
expect(config.levelAdditiveMultiplier).toBe(1);
|
|
||||||
expect(config.pokemonConfigs?.[0].isBoss).toBe(true);
|
expect(config.pokemonConfigs?.[0].isBoss).toBe(true);
|
||||||
expect(onInitResult).toBe(true);
|
expect(onInitResult).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -134,7 +117,7 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: there is some severe test flakiness occurring for this file, needs to be looked at/addressed in separate issue
|
// TODO: there is some severe test flakiness occurring for this file, needs to be looked at/addressed in separate issue
|
||||||
it.skip("should reward the player with X berries based on wave", async () => {
|
it("should reward the player with X berries based on wave", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
||||||
|
|
||||||
const numBerries = game.scene.currentBattle.mysteryEncounter!.misc.numBerries;
|
const numBerries = game.scene.currentBattle.mysteryEncounter!.misc.numBerries;
|
||||||
|
|
|
@ -201,22 +201,6 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||||
expect(BugTypeSuperfanEncounter.options.length).toBe(3);
|
expect(BugTypeSuperfanEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BUG_TYPE_SUPERFAN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should initialize fully", async () => {
|
it("should initialize fully", async () => {
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = BugTypeSuperfanEncounter;
|
scene.currentBattle.mysteryEncounter = BugTypeSuperfanEncounter;
|
||||||
|
|
|
@ -95,14 +95,6 @@ describe("Clowning Around - Mystery Encounter", () => {
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CLOWNING_AROUND);
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CLOWNING_AROUND);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should initialize fully", async () => {
|
it("should initialize fully", async () => {
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = ClowningAroundEncounter;
|
scene.currentBattle.mysteryEncounter = ClowningAroundEncounter;
|
||||||
|
|
|
@ -69,22 +69,6 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
||||||
expect(DancingLessonsEncounter.options.length).toBe(3);
|
expect(DancingLessonsEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DANCING_LESSONS);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not spawn outside of proper biomes", async () => {
|
it("should not spawn outside of proper biomes", async () => {
|
||||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||||
game.override.startingBiome(Biome.SPACE);
|
game.override.startingBiome(Biome.SPACE);
|
||||||
|
|
|
@ -66,22 +66,6 @@ describe("Delibird-y - Mystery Encounter", () => {
|
||||||
expect(DelibirdyEncounter.options.length).toBe(3);
|
expect(DelibirdyEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 10", async () => {
|
|
||||||
game.override.startingWave(9);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DELIBIRDY);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
|
||||||
game.override.startingWave(181);
|
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
|
||||||
|
|
||||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not spawn if player does not have enough money", async () => {
|
it("should not spawn if player does not have enough money", async () => {
|
||||||
scene.money = 0;
|
scene.money = 0;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue