Mystery Encounter bug fixes and minor adjustments (#4299)

* various bug fixes for MEs

* various bug fixes for MEs

* fix final isTransferable rename that was missed

* change Trainer's test vouchers for second option

* change unit test skips

* cut down excess ME track length and loop properly

* ME bug fix cleanup

* updating AI for Slumbering Snorlax ME, and small ME balance changes

* fix ts error

* fix bug type superfan dialogue discrepancy

* ME bug fixes PR feedback

* ME PR nits and fixes

---------

Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
This commit is contained in:
ImperialSympathizer 2024-09-19 09:44:10 -04:00 committed by GitHub
parent 51bb80cb66
commit 5888a7758c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 448 additions and 278 deletions

Binary file not shown.

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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 => {

View File

@ -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;
}
} }
/** /**
@ -6392,7 +6400,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;
} }
@ -7531,7 +7539,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)
@ -8183,7 +8191,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),

View File

@ -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();

View File

@ -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);

View File

@ -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";

View File

@ -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,

View File

@ -19,7 +19,7 @@ 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 { 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";
@ -192,6 +192,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 +287,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 });

View File

@ -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;

View File

@ -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) {
@ -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,

View File

@ -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,

View File

@ -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`,

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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));
} }
}], }],
}; };

View File

@ -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

View File

@ -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;

View File

@ -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";

View File

@ -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,
}); });

View File

@ -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]);
} }
} }

View File

@ -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";

View File

@ -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.

View File

@ -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)

View File

@ -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];

View File

@ -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));

View File

@ -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;

View File

@ -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]);
} }

View File

@ -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 }));

View File

@ -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[];

View File

@ -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
}; };

View File

@ -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";

View File

@ -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() }));
} }
} }

View File

@ -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";

View File

@ -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 {

View File

@ -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

View File

@ -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";
@ -85,21 +85,24 @@ export interface EnemyPokemonConfig {
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 +159,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,7 +213,7 @@ 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));
} }
@ -286,6 +289,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));
@ -702,19 +710,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));
} }
} }
} }

View File

@ -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);
}

View File

@ -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,

View File

@ -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!"
} }

View File

@ -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?",

View File

@ -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"
} }

View File

@ -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;
} }
} }

View File

@ -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;

View File

@ -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));

View File

@ -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,11 +23,15 @@ 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();
} }
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay); 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);
}
} }
end() { end() {

View File

@ -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";

View File

@ -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;
} }

View File

@ -98,7 +98,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 +133,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;

View File

@ -96,7 +96,6 @@ describe("Fight or Flight - Mystery Encounter", () => {
const config = FightOrFlightEncounter.enemyPartyConfigs[0]; const config = FightOrFlightEncounter.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);
}); });

View File

@ -117,12 +117,12 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
}, },
{ {
trainerConfig: expect.any(TrainerConfig), trainerConfig: expect.any(TrainerConfig),
levelAdditiveMultiplier: 1, levelAdditiveModifier: 1,
female: expect.any(Boolean), female: expect.any(Boolean),
}, },
{ {
trainerConfig: expect.any(TrainerConfig), trainerConfig: expect.any(TrainerConfig),
levelAdditiveMultiplier: 1.5, levelAdditiveModifier: 1.5,
female: expect.any(Boolean), female: expect.any(Boolean),
} }
]); ]);

View File

@ -21,6 +21,8 @@ const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
const defaultBiome = Biome.CAVE; const defaultBiome = Biome.CAVE;
const defaultWave = 45; const defaultWave = 45;
const TRANSPORT_BIOMES = [Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE, Biome.WASTELAND, Biome.DOJO];
describe("Teleporting Hijinks - Mystery Encounter", () => { describe("Teleporting Hijinks - Mystery Encounter", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
let game: GameManager; let game: GameManager;
@ -183,7 +185,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(previousBiome).not.toBe(scene.arena.biomeType); expect(previousBiome).not.toBe(scene.arena.biomeType);
expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
}); });
it("should start a battle against an enraged boss", { retry: 5 }, async () => { it("should start a battle against an enraged boss", { retry: 5 }, async () => {
@ -246,7 +248,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
expect(previousBiome).not.toBe(scene.arena.biomeType); expect(previousBiome).not.toBe(scene.arena.biomeType);
expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
}); });
it("should start a battle against an enraged boss", async () => { it("should start a battle against an enraged boss", async () => {

View File

@ -114,7 +114,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([ expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([
{ {
levelAdditiveMultiplier: 1, levelAdditiveModifier: 1,
disableSwitch: true, disableSwitch: true,
pokemonConfigs: [ pokemonConfigs: [
{ {

View File

@ -102,7 +102,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
expect(TrashToTreasureEncounter.enemyPartyConfigs).toEqual([ expect(TrashToTreasureEncounter.enemyPartyConfigs).toEqual([
{ {
levelAdditiveMultiplier: 1, levelAdditiveModifier: 1,
disableSwitch: true, disableSwitch: true,
pokemonConfigs: [ pokemonConfigs: [
{ {

View File

@ -107,7 +107,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
expect(onInitResult).toBe(true); expect(onInitResult).toBe(true);
}); });
describe.skip("Option 1 - Fight", () => { describe("Option 1 - Fight", () => {
it("should have the correct properties", () => { it("should have the correct properties", () => {
const option = UncommonBreedEncounter.options[0]; const option = UncommonBreedEncounter.options[0];
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
@ -123,7 +123,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
}); });
}); });
it("should start a fight against the boss", async () => { it.skip("should start a fight against the boss", async () => {
const phaseSpy = vi.spyOn(scene, "pushPhase"); const phaseSpy = vi.spyOn(scene, "pushPhase");
const unshiftPhaseSpy = vi.spyOn(scene, "unshiftPhase"); const unshiftPhaseSpy = vi.spyOn(scene, "unshiftPhase");
await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty);

View File

@ -160,7 +160,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
this.player = args[0]; this.player = args[0];
const partyHasHeldItem = this.player && !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable).length; const partyHasHeldItem = this.player && !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable).length;
const canLockRarities = !!this.scene.findModifier(m => m instanceof LockModifierTiersModifier); const canLockRarities = !!this.scene.findModifier(m => m instanceof LockModifierTiersModifier);
this.transferButtonContainer.setVisible(false); this.transferButtonContainer.setVisible(false);
@ -277,13 +277,13 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
this.lockRarityButtonContainer.setVisible(canLockRarities); this.lockRarityButtonContainer.setVisible(canLockRarities);
this.scene.tweens.add({ this.scene.tweens.add({
targets: [ this.lockRarityButtonContainer, this.checkButtonContainer, this.continueButtonContainer ], targets: [ this.checkButtonContainer, this.continueButtonContainer ],
alpha: 1, alpha: 1,
duration: 250 duration: 250
}); });
this.scene.tweens.add({ this.scene.tweens.add({
targets: [this.rerollButtonContainer], targets: [this.rerollButtonContainer, this.lockRarityButtonContainer],
alpha: this.rerollCost < 0 ? 0.5 : 1, alpha: this.rerollCost < 0 ? 0.5 : 1,
duration: 250 duration: 250
}); });

View File

@ -6,7 +6,7 @@ import { Button } from "#enums/buttons";
import { addWindow, WindowVariant } from "./ui-theme"; import { addWindow, WindowVariant } from "./ui-theme";
import { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "../phases/mystery-encounter-phases";
import { PartyUiMode } from "./party-ui-handler"; import { PartyUiMode } from "./party-ui-handler";
import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { isNullOrUndefined } from "../utils"; import { isNullOrUndefined } from "../utils";
import { getPokeballAtlasKey } from "../data/pokeball"; import { getPokeballAtlasKey } from "../data/pokeball";
@ -42,7 +42,8 @@ export default class MysteryEncounterUiHandler extends UiHandler {
private encounterOptions: MysteryEncounterOption[] = []; private encounterOptions: MysteryEncounterOption[] = [];
private optionsMeetsReqs: boolean[]; private optionsMeetsReqs: boolean[];
protected viewPartyIndex: integer = 0; protected viewPartyIndex: number = 0;
protected viewPartyXPosition: number = 0;
protected blockInput: boolean = true; protected blockInput: boolean = true;
@ -300,11 +301,11 @@ export default class MysteryEncounterUiHandler extends UiHandler {
} }
} }
override getCursor(): integer { override getCursor(): number {
return this.cursor ? this.cursor : 0; return this.cursor ? this.cursor : 0;
} }
override setCursor(cursor: integer): boolean { override setCursor(cursor: number): boolean {
const prevCursor = this.getCursor(); const prevCursor = this.getCursor();
const changed = prevCursor !== cursor; const changed = prevCursor !== cursor;
if (changed) { if (changed) {
@ -319,7 +320,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
} }
if (cursor === this.viewPartyIndex) { if (cursor === this.viewPartyIndex) {
this.cursorObj.setPosition(246, -17); this.cursorObj.setPosition(this.viewPartyXPosition, -17);
} else if (this.optionsContainer.getAll()?.length === 3) { // 2 Options } else if (this.optionsContainer.getAll()?.length === 3) { // 2 Options
this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15); this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15);
} else if (this.optionsContainer.getAll()?.length === 4) { // 3 Options } else if (this.optionsContainer.getAll()?.length === 4) { // 3 Options
@ -419,8 +420,10 @@ export default class MysteryEncounterUiHandler extends UiHandler {
} }
// View Party Button // View Party Button
const viewPartyText = addBBCodeTextObject(this.scene, 256, -24, getBBCodeFrag(i18next.t("mysteryEncounterMessages:view_party_button"), TextStyle.PARTY), TextStyle.PARTY); const viewPartyText = addBBCodeTextObject(this.scene, (this.scene.game.canvas.width) / 6, -24, getBBCodeFrag(i18next.t("mysteryEncounterMessages:view_party_button"), TextStyle.PARTY), TextStyle.PARTY);
this.optionsContainer.add(viewPartyText); this.optionsContainer.add(viewPartyText);
viewPartyText.x -= (viewPartyText.displayWidth + 16);
this.viewPartyXPosition = viewPartyText.x - 10;
// Description Window // Description Window
const titleTextObject = addBBCodeTextObject(this.scene, 0, 0, titleText ?? "", TextStyle.TOOLTIP_TITLE, { wordWrap: { width: 750 }, align: "center", lineSpacing: -8 }); const titleTextObject = addBBCodeTextObject(this.scene, 0, 0, titleText ?? "", TextStyle.TOOLTIP_TITLE, { wordWrap: { width: 750 }, align: "center", lineSpacing: -8 });

View File

@ -355,7 +355,7 @@ export default class PartyUiHandler extends MessageUiHandler {
const newPokemon = this.scene.getParty()[p]; const newPokemon = this.scene.getParty()[p];
// this next line gets all of the transferable items from pokemon [p]; it does this by getting all the held modifiers that are transferable and checking to see if they belong to pokemon [p] // this next line gets all of the transferable items from pokemon [p]; it does this by getting all the held modifiers that are transferable and checking to see if they belong to pokemon [p]
const getTransferrableItemsFromPokemon = (newPokemon: PlayerPokemon) => const getTransferrableItemsFromPokemon = (newPokemon: PlayerPokemon) =>
this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).isTransferrable && (m as PokemonHeldItemModifier).pokemonId === newPokemon.id) as PokemonHeldItemModifier[]; this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).isTransferable && (m as PokemonHeldItemModifier).pokemonId === newPokemon.id) as PokemonHeldItemModifier[];
// this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon [p]; this returns undefined if the new pokemon doesn't have the item at all, otherwise it returns the pokemonHeldItemModifier for that item // this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon [p]; this returns undefined if the new pokemon doesn't have the item at all, otherwise it returns the pokemonHeldItemModifier for that item
const matchingModifier = newPokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === newPokemon.id && m.matchType(getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor])) as PokemonHeldItemModifier; const matchingModifier = newPokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === newPokemon.id && m.matchType(getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor])) as PokemonHeldItemModifier;
const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us
@ -399,7 +399,7 @@ export default class PartyUiHandler extends MessageUiHandler {
|| (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE)) { || (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE)) {
let filterResult: string | null; let filterResult: string | null;
const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) => const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) =>
this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]; this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[];
if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) {
filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon);
if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) { if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) {
@ -596,7 +596,7 @@ export default class PartyUiHandler extends MessageUiHandler {
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) { if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) {
/** Initialize item quantities for the selected Pokemon */ /** Initialize item quantities for the selected Pokemon */
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.isTransferrable && m.pokemonId === this.scene.getParty()[this.cursor].id) as PokemonHeldItemModifier[]; && m.isTransferable && m.pokemonId === this.scene.getParty()[this.cursor].id) as PokemonHeldItemModifier[];
this.transferQuantities = itemModifiers.map(item => item.getStackCount()); this.transferQuantities = itemModifiers.map(item => item.getStackCount());
this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount()); this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount());
} }
@ -813,7 +813,7 @@ export default class PartyUiHandler extends MessageUiHandler {
const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER
? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier ? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[] && m.isTransferable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]
: []; : [];
if (this.options.length) { if (this.options.length) {