mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-20 16:42:45 +02:00
Compare commits
10 Commits
7ed6d5fce5
...
da41e94451
Author | SHA1 | Date | |
---|---|---|---|
|
da41e94451 | ||
|
4b70fab608 | ||
|
1ff2701964 | ||
|
1e306e25b5 | ||
|
43aa772603 | ||
|
8c01e0264d | ||
|
266eed4d98 | ||
|
27ccd250c2 | ||
|
e407e25392 | ||
|
2264a28300 |
BIN
public/images/heavy_fog.png
Normal file
BIN
public/images/heavy_fog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 432 KiB |
@ -8450,6 +8450,27 @@
|
||||
"w": 16,
|
||||
"h": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "micle_berry",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 20
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 8,
|
||||
"w": 20,
|
||||
"h": 20
|
||||
},
|
||||
"frame": {
|
||||
"x": 400,
|
||||
"y": 386,
|
||||
"w": 20,
|
||||
"h": 20
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 148 KiB |
BIN
public/images/items/micle-berry.png
Normal file
BIN
public/images/items/micle-berry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 304 B |
@ -306,13 +306,6 @@ export class BlockRecoilDamageAttr extends AbAttr {
|
||||
): void {
|
||||
cancelled.value = true;
|
||||
}
|
||||
|
||||
getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) {
|
||||
return i18next.t("abilityTriggers:blockRecoilDamage", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
abilityName: abilityName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8125,7 +8118,7 @@ export function initAbilities() {
|
||||
.unreplaceable()
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.attr(PostSummonFormChangeByWeatherAbAttr, AbilityId.FORECAST)
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]),
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG ]),
|
||||
new Ability(AbilityId.STICKY_HOLD, 3)
|
||||
.attr(BlockItemTheftAbAttr)
|
||||
.bypassFaint()
|
||||
@ -8313,7 +8306,7 @@ export function initAbilities() {
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), AllyStatMultiplierAbAttr, Stat.SPDEF, 1.5)
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.attr(PostSummonFormChangeByWeatherAbAttr, AbilityId.FLOWER_GIFT)
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, AbilityId.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ])
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, AbilityId.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ])
|
||||
.uncopiable()
|
||||
.unreplaceable()
|
||||
.ignorable(),
|
||||
@ -9025,7 +9018,7 @@ export function initAbilities() {
|
||||
.unreplaceable()
|
||||
.ignorable(),
|
||||
new Ability(AbilityId.TERAFORM_ZERO, 9)
|
||||
.attr(ClearWeatherAbAttr, [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN, WeatherType.STRONG_WINDS ])
|
||||
.attr(ClearWeatherAbAttr, [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN, WeatherType.STRONG_WINDS ])
|
||||
.attr(ClearTerrainAbAttr, [ TerrainType.MISTY, TerrainType.ELECTRIC, TerrainType.GRASSY, TerrainType.PSYCHIC ])
|
||||
.uncopiable()
|
||||
.unreplaceable()
|
||||
|
@ -1111,7 +1111,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.SLIGGOO, 40, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]})
|
||||
],
|
||||
[SpeciesId.SLIGGOO]: [
|
||||
new SpeciesEvolution(SpeciesId.GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, SpeciesWildEvolutionDelay.LONG)
|
||||
new SpeciesEvolution(SpeciesId.GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HEAVY_FOG ]}, SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[SpeciesId.BERGMITE]: [
|
||||
new SpeciesEvolution(SpeciesId.HISUI_AVALUGG, 37, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}),
|
||||
@ -1334,7 +1334,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.HISUI_ZOROARK, 30, null, null)
|
||||
],
|
||||
[SpeciesId.HISUI_SLIGGOO]: [
|
||||
new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, SpeciesWildEvolutionDelay.LONG)
|
||||
new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HEAVY_FOG ]}, SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[SpeciesId.SPRIGATITO]: [
|
||||
new SpeciesEvolution(SpeciesId.FLORAGATO, 16, null, null)
|
||||
|
@ -93,6 +93,10 @@ import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap
|
||||
import { applyMoveAttrs } from "./apply-attrs";
|
||||
import { frenzyMissFunc, getMoveTargets } from "./move-utils";
|
||||
|
||||
/**
|
||||
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
|
||||
* Conventionally returns `true` for success and `false` for failure.
|
||||
*/
|
||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||
export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||
|
||||
@ -760,9 +764,6 @@ export default abstract class Move implements Localizable {
|
||||
applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy);
|
||||
applyPreDefendAbAttrs("WonderSkinAbAttr", target, user, this, { value: false }, simulated, moveAccuracy);
|
||||
|
||||
if (moveAccuracy.value === -1) {
|
||||
return moveAccuracy.value;
|
||||
}
|
||||
|
||||
const isOhko = this.hasAttr("OneHitKOAccuracyAttr");
|
||||
|
||||
@ -770,7 +771,11 @@ export default abstract class Move implements Localizable {
|
||||
globalScene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
|
||||
}
|
||||
|
||||
if (globalScene.arena.weather?.weatherType === WeatherType.FOG) {
|
||||
if (moveAccuracy.value === -1) { //Check accuracy after applying items modifier in case of Micle Berry
|
||||
return moveAccuracy.value;
|
||||
}
|
||||
|
||||
if (globalScene.arena.weather?.weatherType === WeatherType.FOG || globalScene.arena.weather?.weatherType === WeatherType.HEAVY_FOG) {
|
||||
/**
|
||||
* The 0.9 multiplier is PokeRogue-only implementation, Bulbapedia uses 3/5
|
||||
* See Fog {@link https://bulbapedia.bulbagarden.net/wiki/Fog}
|
||||
@ -1390,18 +1395,31 @@ export class BeakBlastHeaderAttr extends AddBattlerTagHeaderAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute to display a message before a move is executed.
|
||||
*/
|
||||
export class PreMoveMessageAttr extends MoveAttr {
|
||||
private message: string | ((user: Pokemon, target: Pokemon, move: Move) => string);
|
||||
/** The message to display or a function returning one */
|
||||
private message: string | ((user: Pokemon, target: Pokemon, move: Move) => string | undefined);
|
||||
|
||||
/**
|
||||
* Create a new {@linkcode PreMoveMessageAttr} to display a message before move execution.
|
||||
* @param message - The message to display before move use, either as a string or a function producing one.
|
||||
* @remarks
|
||||
* If {@linkcode message} evaluates to an empty string (`''`), no message will be displayed
|
||||
* (though the move will still succeed).
|
||||
*/
|
||||
constructor(message: string | ((user: Pokemon, target: Pokemon, move: Move) => string)) {
|
||||
super();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const message = typeof this.message === "string"
|
||||
? this.message as string
|
||||
: this.message(user, target, move);
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean {
|
||||
const message = typeof this.message === "function"
|
||||
? this.message(user, target, move)
|
||||
: this.message;
|
||||
|
||||
// TODO: Consider changing if/when MoveAttr `apply` return values become significant
|
||||
if (message) {
|
||||
globalScene.phaseManager.queueMessage(message, 500);
|
||||
return true;
|
||||
@ -9384,7 +9402,7 @@ export function initMoves() {
|
||||
if (!weather) {
|
||||
return 1;
|
||||
}
|
||||
const weatherTypes = [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN ];
|
||||
const weatherTypes = [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN, WeatherType.HEAVY_FOG ];
|
||||
if (weatherTypes.includes(weather.weatherType) && !weather.isEffectSuppressed()) {
|
||||
return 2;
|
||||
}
|
||||
@ -11299,7 +11317,11 @@ export function initMoves() {
|
||||
.attr(ForceSwitchOutAttr, true, SwitchType.SHED_TAIL)
|
||||
.condition(failIfLastInPartyCondition),
|
||||
new SelfStatusMove(MoveId.CHILLY_RECEPTION, PokemonType.ICE, -1, 10, -1, 0, 9)
|
||||
.attr(PreMoveMessageAttr, (user, move) => i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(user) }))
|
||||
.attr(PreMoveMessageAttr, (user, _target, _move) =>
|
||||
// Don't display text if current move phase is follow up (ie move called indirectly)
|
||||
isVirtual((globalScene.phaseManager.getCurrentPhase() as MovePhase).useMode)
|
||||
? ""
|
||||
: i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(user) }))
|
||||
.attr(ChillyReceptionAttr, true),
|
||||
new SelfStatusMove(MoveId.TIDY_UP, PokemonType.NORMAL, -1, 10, -1, 0, 9)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true)
|
||||
|
449
src/data/mystery-encounters/encounters/creeping-fog-encounter.ts
Normal file
449
src/data/mystery-encounters/encounters/creeping-fog-encounter.ts
Normal file
@ -0,0 +1,449 @@
|
||||
import type { EnemyPartyConfig, EnemyPokemonConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
initBattleWithEnemyConfig,
|
||||
setEncounterRewards,
|
||||
leaveEncounterWithoutBattle,
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
generateModifierType,
|
||||
setEncounterExp,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { TimeOfDayRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import type { AbilityId } from "#enums/ability-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import {
|
||||
MoveRequirement,
|
||||
AbilityRequirement,
|
||||
CombinationPokemonRequirement,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import {
|
||||
DEFOG_MOVES,
|
||||
DEFOG_ABILITIES,
|
||||
LIGHT_ABILITIES,
|
||||
LIGHT_MOVES,
|
||||
} from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import FogOverlay from "#app/ui/fog-overlay";
|
||||
|
||||
// the i18n namespace for the encounter
|
||||
const namespace = "mysteryEncounters/creepingFog";
|
||||
|
||||
/**
|
||||
* Creeping Fog Mystery Encounter.
|
||||
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/4418 | GitHub Issue #4418}
|
||||
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||
**/
|
||||
export const CreepingFogEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
||||
MysteryEncounterType.CREEPING_FOG,
|
||||
)
|
||||
.withSceneRequirement(new TimeOfDayRequirement([TimeOfDay.DUSK, TimeOfDay.DAWN, TimeOfDay.NIGHT]))
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withSceneWaveRangeRequirement(51, 179)
|
||||
.withFleeAllowed(false)
|
||||
.withIntroSpriteConfigs([])
|
||||
.withIntroDialogue([
|
||||
{
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit(() => {
|
||||
const waveIndex = globalScene.currentBattle.waveIndex;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const chosenPokemonAttributes = chooseBoss();
|
||||
const chosenPokemon = chosenPokemonAttributes[0] as SpeciesId;
|
||||
const naturePokemon = chosenPokemonAttributes[1] as Nature;
|
||||
const abilityPokemon = chosenPokemonAttributes[2] as AbilityId;
|
||||
const passivePokemon = chosenPokemon[3] as boolean;
|
||||
const movesPokemon = chosenPokemonAttributes[4] as MoveId[];
|
||||
const modifPokemon = chosenPokemonAttributes[5] as HeldModifierConfig[];
|
||||
const segments = waveIndex < 80 ? 2 : waveIndex < 140 ? 3 : 4;
|
||||
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: getPokemonSpecies(chosenPokemon),
|
||||
formIndex: [SpeciesId.LYCANROC, SpeciesId.PIDGEOT].includes(chosenPokemon) ? 1 : 0,
|
||||
isBoss: true,
|
||||
shiny: false,
|
||||
customPokemonData: new CustomPokemonData({ spriteScale: 1 + segments * 0.05 }),
|
||||
nature: naturePokemon,
|
||||
moveSet: movesPokemon,
|
||||
abilityIndex: abilityPokemon,
|
||||
passive: passivePokemon,
|
||||
bossSegments: segments,
|
||||
modifierConfigs: modifPokemon,
|
||||
};
|
||||
|
||||
const config: EnemyPartyConfig = {
|
||||
levelAdditiveModifier: 0.5,
|
||||
pokemonConfigs: [pokemonConfig],
|
||||
};
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
encounter.spriteConfigs = [
|
||||
{
|
||||
spriteKey: chosenPokemon.toString(),
|
||||
fileRoot: "pokemon",
|
||||
repeat: true,
|
||||
hasShadow: true,
|
||||
hidden: true,
|
||||
x: 0,
|
||||
tint: 1,
|
||||
y: 0,
|
||||
yShadow: -3,
|
||||
},
|
||||
];
|
||||
|
||||
const overlayWidth = globalScene.game.canvas.width / 6;
|
||||
const overlayHeight = globalScene.game.canvas.height / 6 - 48;
|
||||
const fogOverlay = new FogOverlay({
|
||||
delayVisibility: false,
|
||||
scale: 1,
|
||||
onSide: true,
|
||||
right: true,
|
||||
x: 1,
|
||||
y: overlayHeight * -1 - 48,
|
||||
width: overlayWidth,
|
||||
height: overlayHeight,
|
||||
});
|
||||
encounter.misc = {
|
||||
fogOverlay,
|
||||
};
|
||||
globalScene.ui.add(fogOverlay);
|
||||
globalScene.ui.sendToBack(fogOverlay);
|
||||
globalScene.tweens.add({
|
||||
targets: fogOverlay,
|
||||
alpha: 0.5,
|
||||
ease: "Sine.easeIn",
|
||||
duration: 2000,
|
||||
});
|
||||
fogOverlay.active = true;
|
||||
fogOverlay.setVisible(true);
|
||||
|
||||
return true;
|
||||
})
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}:option.1.label`,
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.1.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.tweens.add({
|
||||
targets: encounter.misc.fogOverlay,
|
||||
alpha: 0,
|
||||
ease: "Sine.easeOut",
|
||||
duration: 2000,
|
||||
});
|
||||
})
|
||||
.withOptionPhase(async () => {
|
||||
//Battle Fog Boss
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.arena.trySetWeather(WeatherType.HEAVY_FOG);
|
||||
//TODO start fog and stuff
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||
fillRemaining: true,
|
||||
});
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||
.withPrimaryPokemonRequirement(
|
||||
CombinationPokemonRequirement.Some(
|
||||
new MoveRequirement(DEFOG_MOVES, true),
|
||||
new AbilityRequirement(DEFOG_ABILITIES, true),
|
||||
),
|
||||
)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.2.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.tweens.add({
|
||||
targets: encounter.misc.fogOverlay,
|
||||
alpha: 0,
|
||||
ease: "Sine.easeOut",
|
||||
duration: 2000,
|
||||
});
|
||||
})
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const primary = encounter.options[1].primaryPokemon!;
|
||||
if (globalScene.currentBattle.waveIndex >= 140) {
|
||||
setEncounterExp([primary.id], encounter.enemyPartyConfigs![0].pokemonConfigs![0].species.baseExp);
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||
fillRemaining: true,
|
||||
});
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
await initBattleWithEnemyConfig(config);
|
||||
} else {
|
||||
setEncounterExp([primary.id], encounter.enemyPartyConfigs![0].pokemonConfigs![0].species.baseExp);
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||
.withPrimaryPokemonRequirement(
|
||||
CombinationPokemonRequirement.Some(
|
||||
new MoveRequirement(LIGHT_MOVES, true),
|
||||
new AbilityRequirement(LIGHT_ABILITIES, true),
|
||||
),
|
||||
)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}:option.3.label`,
|
||||
buttonTooltip: `${namespace}:option.3.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.3.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.tweens.add({
|
||||
targets: encounter.misc.fogOverlay,
|
||||
alpha: 0,
|
||||
ease: "Sine.easeOut",
|
||||
duration: 2000,
|
||||
});
|
||||
})
|
||||
.withOptionPhase(async () => {
|
||||
//Navigate through the Fog
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const primary = encounter.options[2].primaryPokemon!;
|
||||
globalScene.arena.trySetWeather(WeatherType.HEAVY_FOG);
|
||||
if (globalScene.currentBattle.waveIndex >= 140) {
|
||||
setEncounterExp([primary.id], encounter.enemyPartyConfigs![0].pokemonConfigs![0].species.baseExp);
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||
fillRemaining: true,
|
||||
});
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
await initBattleWithEnemyConfig(config);
|
||||
} else {
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [ModifierTier.ULTRA],
|
||||
fillRemaining: true,
|
||||
});
|
||||
setEncounterExp([primary.id], encounter.enemyPartyConfigs![0].pokemonConfigs![0].species.baseExp);
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
|
||||
.withSimpleOption(
|
||||
{
|
||||
buttonLabel: `${namespace}:option.4.label`,
|
||||
buttonTooltip: `${namespace}:option.4.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.4.selected`,
|
||||
},
|
||||
],
|
||||
},
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.tweens.add({
|
||||
targets: encounter.misc.fogOverlay,
|
||||
alpha: 0,
|
||||
ease: "Sine.easeOut",
|
||||
duration: 2000,
|
||||
});
|
||||
const pokemon = globalScene.getPlayerPokemon(); //Can we use this?
|
||||
globalScene.arena.trySetWeather(WeatherType.FOG, pokemon);
|
||||
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
},
|
||||
)
|
||||
.build();
|
||||
|
||||
function chooseBoss() {
|
||||
const biome = globalScene.arena.biomeType;
|
||||
const wave = globalScene.currentBattle.waveIndex;
|
||||
const allBiomePokemon = [
|
||||
[
|
||||
SpeciesId.MACHAMP,
|
||||
Nature.JOLLY,
|
||||
1,
|
||||
false,
|
||||
[MoveId.DYNAMIC_PUNCH, MoveId.STONE_EDGE, MoveId.DUAL_CHOP, MoveId.FISSURE],
|
||||
[],
|
||||
],
|
||||
[
|
||||
SpeciesId.GRIMMSNARL,
|
||||
Nature.ADAMANT,
|
||||
null,
|
||||
false,
|
||||
[MoveId.STONE_EDGE, MoveId.CLOSE_COMBAT, MoveId.IRON_TAIL, MoveId.PLAY_ROUGH],
|
||||
[{ modifier: generateModifierType(modifierTypes.MICLE_BERRY) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
];
|
||||
const ForestTallGrassPokemon = [
|
||||
[
|
||||
SpeciesId.LYCANROC,
|
||||
Nature.JOLLY,
|
||||
2,
|
||||
false,
|
||||
[MoveId.STONE_EDGE, MoveId.CLOSE_COMBAT, MoveId.IRON_TAIL, MoveId.PLAY_ROUGH],
|
||||
[],
|
||||
],
|
||||
[
|
||||
SpeciesId.ALOLA_RATICATE,
|
||||
Nature.ADAMANT,
|
||||
1,
|
||||
false,
|
||||
[MoveId.FALSE_SURRENDER, MoveId.SUCKER_PUNCH, MoveId.PLAY_ROUGH, MoveId.POPULATION_BOMB],
|
||||
[{ modifier: generateModifierType(modifierTypes.REVIVER_SEED) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
];
|
||||
const SwampLakePokemon = [
|
||||
[
|
||||
SpeciesId.POLIWRATH,
|
||||
Nature.NAIVE,
|
||||
null,
|
||||
true,
|
||||
[MoveId.DYNAMIC_PUNCH, MoveId.HYDRO_PUMP, MoveId.DUAL_CHOP, MoveId.HYPNOSIS],
|
||||
[
|
||||
{ modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType },
|
||||
{ modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType },
|
||||
],
|
||||
],
|
||||
];
|
||||
const GraveyardPokemon = [
|
||||
[
|
||||
SpeciesId.GOLURK,
|
||||
Nature.ADAMANT,
|
||||
2,
|
||||
false,
|
||||
[MoveId.EARTHQUAKE, MoveId.POLTERGEIST, MoveId.DYNAMIC_PUNCH, MoveId.STONE_EDGE],
|
||||
[],
|
||||
],
|
||||
[
|
||||
SpeciesId.HONEDGE,
|
||||
Nature.CAREFUL,
|
||||
0,
|
||||
false,
|
||||
[MoveId.IRON_HEAD, MoveId.POLTERGEIST, MoveId.SACRED_SWORD, MoveId.SHADOW_SNEAK],
|
||||
[],
|
||||
],
|
||||
[
|
||||
SpeciesId.ZWEILOUS,
|
||||
Nature.BRAVE,
|
||||
null,
|
||||
true,
|
||||
[MoveId.DRAGON_RUSH, MoveId.CRUNCH, MoveId.GUNK_SHOT, MoveId.SCREECH],
|
||||
[{ modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType, stackCount: 2 }],
|
||||
],
|
||||
];
|
||||
const wave110_140Pokemon = [
|
||||
[
|
||||
SpeciesId.SCOLIPEDE,
|
||||
Nature.ADAMANT,
|
||||
2,
|
||||
false,
|
||||
[MoveId.MEGAHORN, MoveId.NOXIOUS_TORQUE, MoveId.ROLLOUT, MoveId.BANEFUL_BUNKER],
|
||||
[{ modifier: generateModifierType(modifierTypes.MICLE_BERRY) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
[
|
||||
SpeciesId.MIENSHAO,
|
||||
Nature.JOLLY,
|
||||
null,
|
||||
true,
|
||||
[MoveId.HIGH_JUMP_KICK, MoveId.STONE_EDGE, MoveId.BLAZE_KICK, MoveId.GUNK_SHOT],
|
||||
[],
|
||||
],
|
||||
[
|
||||
SpeciesId.DRACOZOLT,
|
||||
Nature.JOLLY,
|
||||
null,
|
||||
true,
|
||||
[MoveId.BOLT_BEAK, MoveId.DRAGON_RUSH, MoveId.EARTHQUAKE, MoveId.STONE_EDGE],
|
||||
[],
|
||||
],
|
||||
];
|
||||
const wave140PlusPokemon = [
|
||||
[
|
||||
SpeciesId.PIDGEOT,
|
||||
Nature.HASTY,
|
||||
0,
|
||||
false,
|
||||
[MoveId.HURRICANE, MoveId.HEAT_WAVE, MoveId.FOCUS_BLAST, MoveId.WILDBOLT_STORM],
|
||||
[],
|
||||
],
|
||||
];
|
||||
|
||||
let pool = allBiomePokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][];
|
||||
|
||||
// Include biome-specific Pokémon if within wave 50-80
|
||||
if (wave >= 50) {
|
||||
if (biome === BiomeId.FOREST || biome === BiomeId.TALL_GRASS) {
|
||||
pool = pool.concat(
|
||||
ForestTallGrassPokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][],
|
||||
);
|
||||
}
|
||||
if (biome === BiomeId.SWAMP || biome === BiomeId.LAKE) {
|
||||
pool = pool.concat(SwampLakePokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][]);
|
||||
}
|
||||
if (biome === BiomeId.GRAVEYARD) {
|
||||
pool = pool.concat(GraveyardPokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][]);
|
||||
}
|
||||
}
|
||||
|
||||
// Waves 110-140 content
|
||||
if (wave >= 110) {
|
||||
pool = pool.concat(wave110_140Pokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][]);
|
||||
}
|
||||
|
||||
// Wave 140+
|
||||
if (wave >= 140) {
|
||||
pool = pool.concat(wave140PlusPokemon as [SpeciesId, Nature, AbilityId, boolean, MoveId[], HeldModifierConfig[]][]);
|
||||
}
|
||||
// Randomly choose one
|
||||
return pool[randSeedInt(pool.length, 0)];
|
||||
}
|
@ -32,6 +32,7 @@ import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fu
|
||||
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
||||
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
|
||||
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||
import { CreepingFogEncounter } from "#app/data/mystery-encounters/encounters/creeping-fog-encounter";
|
||||
import { getBiomeName } from "#app/data/balance/biomes";
|
||||
|
||||
export const EXTREME_ENCOUNTER_BIOMES = [
|
||||
@ -192,11 +193,11 @@ export const mysteryEncountersByBiome = new Map<BiomeId, MysteryEncounterType[]>
|
||||
[BiomeId.TOWN, []],
|
||||
[BiomeId.PLAINS, [MysteryEncounterType.SLUMBERING_SNORLAX]],
|
||||
[BiomeId.GRASS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE]],
|
||||
[BiomeId.TALL_GRASS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE]],
|
||||
[BiomeId.TALL_GRASS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE, MysteryEncounterType.CREEPING_FOG]],
|
||||
[BiomeId.METROPOLIS, []],
|
||||
[BiomeId.FOREST, [MysteryEncounterType.SAFARI_ZONE, MysteryEncounterType.ABSOLUTE_AVARICE]],
|
||||
[BiomeId.FOREST, [MysteryEncounterType.SAFARI_ZONE, MysteryEncounterType.ABSOLUTE_AVARICE, MysteryEncounterType.CREEPING_FOG]],
|
||||
[BiomeId.SEA, [MysteryEncounterType.LOST_AT_SEA]],
|
||||
[BiomeId.SWAMP, [MysteryEncounterType.SAFARI_ZONE]],
|
||||
[BiomeId.SWAMP, [MysteryEncounterType.SAFARI_ZONE, MysteryEncounterType.CREEPING_FOG]],
|
||||
[BiomeId.BEACH, []],
|
||||
[BiomeId.LAKE, []],
|
||||
[BiomeId.SEABED, []],
|
||||
@ -208,7 +209,7 @@ export const mysteryEncountersByBiome = new Map<BiomeId, MysteryEncounterType[]>
|
||||
[BiomeId.MEADOW, []],
|
||||
[BiomeId.POWER_PLANT, []],
|
||||
[BiomeId.VOLCANO, [MysteryEncounterType.FIERY_FALLOUT, MysteryEncounterType.DANCING_LESSONS]],
|
||||
[BiomeId.GRAVEYARD, []],
|
||||
[BiomeId.GRAVEYARD, [MysteryEncounterType.CREEPING_FOG]],
|
||||
[BiomeId.DOJO, []],
|
||||
[BiomeId.FACTORY, []],
|
||||
[BiomeId.RUINS, []],
|
||||
@ -257,6 +258,7 @@ export function initMysteryEncounters() {
|
||||
allMysteryEncounters[MysteryEncounterType.UNCOMMON_BREED] = UncommonBreedEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.GLOBAL_TRADE_SYSTEM] = GlobalTradeSystemEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER] = TheExpertPokemonBreederEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.CREEPING_FOG] = CreepingFogEncounter;
|
||||
|
||||
// Add extreme encounters to biome map
|
||||
extremeBiomeEncounters.forEach(encounter => {
|
||||
|
@ -108,6 +108,16 @@ export const EXTORTION_MOVES = [
|
||||
MoveId.STRING_SHOT,
|
||||
];
|
||||
|
||||
/**
|
||||
* Moves that can clear a foggy weather
|
||||
*/
|
||||
export const DEFOG_MOVES = [MoveId.DEFOG, MoveId.RAPID_SPIN, MoveId.GUST];
|
||||
|
||||
/**
|
||||
* Moves that can help navigate through foggy weather
|
||||
*/
|
||||
export const LIGHT_MOVES = [MoveId.FLASH, MoveId.FORESIGHT];
|
||||
|
||||
/**
|
||||
* Abilities that (loosely) can be used to trap/rob someone
|
||||
*/
|
||||
@ -135,3 +145,18 @@ export const FIRE_RESISTANT_ABILITIES = [
|
||||
AbilityId.STEAM_ENGINE,
|
||||
AbilityId.PRIMORDIAL_SEA,
|
||||
];
|
||||
|
||||
/**
|
||||
* Abilities that can clear foggy weather
|
||||
*/
|
||||
export const DEFOG_ABILITIES = [AbilityId.AIR_LOCK, AbilityId.CLOUD_NINE];
|
||||
|
||||
/**
|
||||
* Abilities that can help navigate through foggy weather
|
||||
*/
|
||||
export const LIGHT_ABILITIES = [
|
||||
AbilityId.KEEN_EYE,
|
||||
AbilityId.ILLUMINATE,
|
||||
AbilityId.COMPOUND_EYES,
|
||||
AbilityId.VICTORY_STAR,
|
||||
];
|
||||
|
@ -751,7 +751,7 @@ export async function catchPokemon(
|
||||
UiMode.POKEDEX_PAGE,
|
||||
pokemon.species,
|
||||
pokemon.formIndex,
|
||||
attributes,
|
||||
[attributes],
|
||||
null,
|
||||
() => {
|
||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
|
||||
|
@ -256,9 +256,9 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "", "snowy", new SpeciesFormChangeWeatherTrigger(AbilityId.FORECAST, [ WeatherType.HAIL, WeatherType.SNOW ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "sunny", "snowy", new SpeciesFormChangeWeatherTrigger(AbilityId.FORECAST, [ WeatherType.HAIL, WeatherType.SNOW ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "rainy", "snowy", new SpeciesFormChangeWeatherTrigger(AbilityId.FORECAST, [ WeatherType.HAIL, WeatherType.SNOW ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "sunny", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(SpeciesId.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true)
|
||||
@ -300,7 +300,7 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
],
|
||||
[SpeciesId.CHERRIM]: [
|
||||
new SpeciesFormChange(SpeciesId.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(AbilityId.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(AbilityId.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HEAVY_FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true),
|
||||
new SpeciesFormChange(SpeciesId.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true)
|
||||
],
|
||||
[SpeciesId.LOPUNNY]: [
|
||||
|
@ -764,7 +764,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
readonly subLegendary: boolean;
|
||||
readonly legendary: boolean;
|
||||
readonly mythical: boolean;
|
||||
readonly species: string;
|
||||
public category: string;
|
||||
readonly growthRate: GrowthRate;
|
||||
/** The chance (as a decimal) for this Species to be male, or `null` for genderless species */
|
||||
readonly malePercent: number | null;
|
||||
@ -778,7 +778,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
subLegendary: boolean,
|
||||
legendary: boolean,
|
||||
mythical: boolean,
|
||||
species: string,
|
||||
category: string,
|
||||
type1: PokemonType,
|
||||
type2: PokemonType | null,
|
||||
height: number,
|
||||
@ -829,7 +829,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
this.subLegendary = subLegendary;
|
||||
this.legendary = legendary;
|
||||
this.mythical = mythical;
|
||||
this.species = species;
|
||||
this.category = category;
|
||||
this.growthRate = growthRate;
|
||||
this.malePercent = malePercent;
|
||||
this.genderDiffs = genderDiffs;
|
||||
@ -968,6 +968,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
|
||||
localize(): void {
|
||||
this.name = i18next.t(`pokemon:${SpeciesId[this.speciesId].toLowerCase()}`);
|
||||
this.category = i18next.t(`pokemonCategory:${SpeciesId[this.speciesId].toLowerCase()}_category`);
|
||||
}
|
||||
|
||||
getWildSpeciesForLevel(level: number, allowEvolving: boolean, isBoss: boolean, gameMode: GameMode): SpeciesId {
|
||||
|
@ -37,6 +37,7 @@ export class Weather {
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
case WeatherType.HARSH_SUN:
|
||||
case WeatherType.STRONG_WINDS:
|
||||
case WeatherType.HEAVY_FOG:
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -137,6 +138,8 @@ export function getWeatherStartMessage(weatherType: WeatherType): string | null
|
||||
return i18next.t("weather:snowStartMessage");
|
||||
case WeatherType.FOG:
|
||||
return i18next.t("weather:fogStartMessage");
|
||||
case WeatherType.HEAVY_FOG:
|
||||
return i18next.t("weather:heavyFogStartMessage");
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
return i18next.t("weather:heavyRainStartMessage");
|
||||
case WeatherType.HARSH_SUN:
|
||||
@ -162,6 +165,8 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string | null
|
||||
return i18next.t("weather:snowLapseMessage");
|
||||
case WeatherType.FOG:
|
||||
return i18next.t("weather:fogLapseMessage");
|
||||
case WeatherType.HEAVY_FOG:
|
||||
return i18next.t("weather:heavyFogLapseMessage");
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
return i18next.t("weather:heavyRainLapseMessage");
|
||||
case WeatherType.HARSH_SUN:
|
||||
@ -202,6 +207,8 @@ export function getWeatherClearMessage(weatherType: WeatherType): string | null
|
||||
return i18next.t("weather:snowClearMessage");
|
||||
case WeatherType.FOG:
|
||||
return i18next.t("weather:fogClearMessage");
|
||||
case WeatherType.HEAVY_FOG:
|
||||
return i18next.t("weather:fogClearMessage");
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
return i18next.t("weather:heavyRainClearMessage");
|
||||
case WeatherType.HARSH_SUN:
|
||||
@ -221,6 +228,8 @@ export function getLegendaryWeatherContinuesMessage(weatherType: WeatherType): s
|
||||
return i18next.t("weather:heavyRainContinueMessage");
|
||||
case WeatherType.STRONG_WINDS:
|
||||
return i18next.t("weather:strongWindsContinueMessage");
|
||||
case WeatherType.HEAVY_FOG:
|
||||
return i18next.t("weather:heavyFogContinueMessage");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -29,5 +29,6 @@ export enum MysteryEncounterType {
|
||||
FUN_AND_GAMES,
|
||||
UNCOMMON_BREED,
|
||||
GLOBAL_TRADE_SYSTEM,
|
||||
THE_EXPERT_POKEMON_BREEDER
|
||||
THE_EXPERT_POKEMON_BREEDER,
|
||||
CREEPING_FOG
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ export enum WeatherType {
|
||||
HEAVY_RAIN,
|
||||
HARSH_SUN,
|
||||
STRONG_WINDS,
|
||||
HEAVY_FOG,
|
||||
}
|
||||
|
@ -327,8 +327,15 @@ export class Arena {
|
||||
|
||||
if (
|
||||
this.weather?.isImmutable() &&
|
||||
![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather)
|
||||
![
|
||||
WeatherType.HARSH_SUN,
|
||||
WeatherType.HEAVY_RAIN,
|
||||
WeatherType.STRONG_WINDS,
|
||||
WeatherType.HEAVY_FOG,
|
||||
WeatherType.NONE,
|
||||
].includes(weather)
|
||||
) {
|
||||
if (oldWeatherType !== WeatherType.HEAVY_FOG) {
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
undefined,
|
||||
@ -336,6 +343,7 @@ export class Arena {
|
||||
CommonAnim.SUNNY + (oldWeatherType - 1),
|
||||
true,
|
||||
);
|
||||
}
|
||||
globalScene.phaseManager.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!);
|
||||
return false;
|
||||
}
|
||||
|
@ -236,6 +236,7 @@ export class LoadingScene extends SceneBase {
|
||||
|
||||
this.loadAtlas("pb", "");
|
||||
this.loadAtlas("items", "");
|
||||
this.loadImage("heavy_fog", "");
|
||||
this.loadAtlas("types", "");
|
||||
|
||||
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_<lang>
|
||||
|
@ -1147,6 +1147,9 @@ export class PokemonMoveAccuracyBoosterModifierType extends PokemonHeldItemModif
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
if (this.amount === -1) {
|
||||
return i18next.t("modifierType:ModifierType.MICLE_BERRY.description");
|
||||
}
|
||||
return i18next.t("modifierType:ModifierType.PokemonMoveAccuracyBoosterModifierType.description", {
|
||||
accuracyAmount: this.amount,
|
||||
});
|
||||
@ -2141,6 +2144,8 @@ const modifierTypeInitObj = Object.freeze({
|
||||
GRIP_CLAW: () =>
|
||||
new ContactHeldItemTransferChanceModifierType("modifierType:ModifierType.GRIP_CLAW", "grip_claw", 10),
|
||||
WIDE_LENS: () => new PokemonMoveAccuracyBoosterModifierType("modifierType:ModifierType.WIDE_LENS", "wide_lens", 5),
|
||||
MICLE_BERRY: () =>
|
||||
new PokemonMoveAccuracyBoosterModifierType("modifierType:ModifierType.MICLE_BERRY", "micle_berry", -1),
|
||||
|
||||
MULTI_LENS: () => new PokemonMultiHitModifierType("modifierType:ModifierType.MULTI_LENS", "zoom_lens"),
|
||||
|
||||
|
@ -2739,8 +2739,11 @@ export class PokemonMoveAccuracyBoosterModifier extends PokemonHeldItemModifier
|
||||
* @returns always `true`
|
||||
*/
|
||||
override apply(_pokemon: Pokemon, moveAccuracy: NumberHolder): boolean {
|
||||
moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount();
|
||||
|
||||
if (this.accuracyAmount !== -1) {
|
||||
moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount();
|
||||
} else {
|
||||
moveAccuracy.value = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -668,6 +668,9 @@ export class MovePhase extends BattlePhase {
|
||||
}),
|
||||
500,
|
||||
);
|
||||
|
||||
// Moves with pre-use messages (Magnitude, Chilly Reception, Fickle Beam, etc.) always display their messages even on failure
|
||||
// TODO: This assumes single target for message funcs - is this sustainable?
|
||||
applyMoveAttrs("PreMoveMessageAttr", this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove());
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,7 @@ export async function initI18n(): Promise<void> {
|
||||
"pokeball",
|
||||
"pokedexUiHandler",
|
||||
"pokemon",
|
||||
"pokemonCategory",
|
||||
"pokemonEvolutions",
|
||||
"pokemonForm",
|
||||
"pokemonInfo",
|
||||
@ -298,6 +299,7 @@ export async function initI18n(): Promise<void> {
|
||||
"mysteryEncounters/uncommonBreed",
|
||||
"mysteryEncounters/globalTradeSystem",
|
||||
"mysteryEncounters/theExpertPokemonBreeder",
|
||||
"mysteryEncounters/creepingFog",
|
||||
"mysteryEncounterMessages",
|
||||
],
|
||||
detection: {
|
||||
|
52
src/ui/fog-overlay.ts
Normal file
52
src/ui/fog-overlay.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export interface FogOverlaySettings {
|
||||
delayVisibility?: boolean;
|
||||
scale?: number;
|
||||
top?: boolean;
|
||||
right?: boolean;
|
||||
onSide?: boolean;
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const EFF_HEIGHT = 48;
|
||||
const EFF_WIDTH = 82;
|
||||
|
||||
export default class FogOverlay extends Phaser.GameObjects.Container {
|
||||
public active = false;
|
||||
|
||||
private val: Phaser.GameObjects.Container;
|
||||
private typ: Phaser.GameObjects.Sprite;
|
||||
|
||||
constructor(options?: FogOverlaySettings) {
|
||||
if (options?.onSide) {
|
||||
options.top = false;
|
||||
}
|
||||
super(globalScene, options?.x, options?.y);
|
||||
const scale = options?.scale || 1; // set up the scale
|
||||
this.setScale(scale);
|
||||
|
||||
this.val = new Phaser.GameObjects.Container(
|
||||
globalScene,
|
||||
options?.onSide && !options?.right ? EFF_WIDTH : 0,
|
||||
options?.top ? EFF_HEIGHT : 0,
|
||||
);
|
||||
this.typ = globalScene.add.sprite(25, EFF_HEIGHT - 35, "heavy_fog");
|
||||
this.typ.setAlpha(1);
|
||||
this.setAlpha(0);
|
||||
this.typ.setScale(0.8);
|
||||
this.val.add(this.typ);
|
||||
this.add(this.val);
|
||||
this.setVisible(false);
|
||||
}
|
||||
clear() {
|
||||
this.setVisible(false);
|
||||
this.active = false;
|
||||
}
|
||||
isActive(): boolean {
|
||||
return this.active;
|
||||
}
|
||||
}
|
@ -174,6 +174,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container;
|
||||
private pokemonCaughtCountText: Phaser.GameObjects.Text;
|
||||
private pokemonFormText: Phaser.GameObjects.Text;
|
||||
private pokemonCategoryText: Phaser.GameObjects.Text;
|
||||
private pokemonHatchedIcon: Phaser.GameObjects.Sprite;
|
||||
private pokemonHatchedCountText: Phaser.GameObjects.Text;
|
||||
private pokemonShinyIcons: Phaser.GameObjects.Sprite[];
|
||||
@ -409,6 +410,12 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.pokemonFormText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonFormText);
|
||||
|
||||
this.pokemonCategoryText = addTextObject(100, 18, "Category", TextStyle.WINDOW_ALT, {
|
||||
fontSize: "42px",
|
||||
});
|
||||
this.pokemonCategoryText.setOrigin(1, 0);
|
||||
this.starterSelectContainer.add(this.pokemonCategoryText);
|
||||
|
||||
this.pokemonCaughtHatchedContainer = globalScene.add.container(2, 25);
|
||||
this.pokemonCaughtHatchedContainer.setScale(0.5);
|
||||
this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer);
|
||||
@ -2354,6 +2361,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.pokemonCaughtHatchedContainer.setVisible(true);
|
||||
this.pokemonCandyContainer.setVisible(false);
|
||||
this.pokemonFormText.setVisible(false);
|
||||
this.pokemonCategoryText.setVisible(false);
|
||||
|
||||
const defaultDexAttr = globalScene.gameData.getSpeciesDefaultDexAttr(species, true, true);
|
||||
const props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
|
||||
@ -2382,6 +2390,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.pokemonCaughtHatchedContainer.setVisible(false);
|
||||
this.pokemonCandyContainer.setVisible(false);
|
||||
this.pokemonFormText.setVisible(false);
|
||||
this.pokemonCategoryText.setVisible(false);
|
||||
|
||||
this.setSpeciesDetails(species!, {
|
||||
// TODO: is this bang correct?
|
||||
@ -2534,6 +2543,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.pokemonNameText.setText(species ? "???" : "");
|
||||
}
|
||||
|
||||
// Setting the category
|
||||
if (isFormCaught) {
|
||||
this.pokemonCategoryText.setText(species.category);
|
||||
} else {
|
||||
this.pokemonCategoryText.setText("");
|
||||
}
|
||||
|
||||
// Setting tint of the sprite
|
||||
if (isFormCaught) {
|
||||
this.species.loadAssets(female!, formIndex, shiny, variant as Variant, true).then(() => {
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { RandomMoveAttr } from "#app/data/moves/move";
|
||||
import { MoveResult } from "#enums/move-result";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { AbilityId } from "#app/enums/ability-id";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import i18next from "i18next";
|
||||
import Phaser from "phaser";
|
||||
//import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("Moves - Chilly Reception", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -25,95 +28,121 @@ describe("Moves - Chilly Reception", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.moveset([MoveId.CHILLY_RECEPTION, MoveId.SNOWSCAPE])
|
||||
.moveset([MoveId.CHILLY_RECEPTION, MoveId.SNOWSCAPE, MoveId.SPLASH, MoveId.METRONOME])
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.ability(AbilityId.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should still change the weather if user can't switch out", async () => {
|
||||
it("should display message before use, switch the user out and change the weather to snow", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING, SpeciesId.MEOWTH]);
|
||||
|
||||
const [slowking, meowth] = game.scene.getPlayerParty();
|
||||
|
||||
game.move.select(MoveId.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getPlayerPokemon()).toBe(meowth);
|
||||
expect(slowking.isOnField()).toBe(false);
|
||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||
expect(game.textInterceptor.logs).toContain(
|
||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||
);
|
||||
});
|
||||
|
||||
it("should still change weather if user can't switch out", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING]);
|
||||
|
||||
game.move.select(MoveId.CHILLY_RECEPTION);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||
expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||
});
|
||||
|
||||
it("should switch out even if it's snowing", async () => {
|
||||
it("should still switch out even if weather cannot be changed", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING, SpeciesId.MEOWTH]);
|
||||
// first turn set up snow with snowscape, try chilly reception on second turn
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SNOW);
|
||||
|
||||
const [slowking, meowth] = game.scene.getPlayerParty();
|
||||
|
||||
game.move.select(MoveId.SNOWSCAPE);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
game.move.select(MoveId.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
// TODO: Uncomment lines once wimp out PR fixes force switches to not reset summon data immediately
|
||||
// await game.phaseInterceptor.to("SwitchSummonPhase", false);
|
||||
// expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||
|
||||
await game.toEndOfTurn();
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MEOWTH);
|
||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||
expect(game.scene.getPlayerPokemon()).toBe(meowth);
|
||||
expect(slowking.isOnField()).toBe(false);
|
||||
});
|
||||
|
||||
it("happy case - switch out and weather changes", async () => {
|
||||
// Source: https://replay.pokemonshowdown.com/gen9ou-2367532550
|
||||
it("should fail (while still displaying message) if neither weather change nor switch out succeeds", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING]);
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SNOW);
|
||||
|
||||
const slowking = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(MoveId.SNOWSCAPE);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
|
||||
game.move.select(MoveId.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||
expect(game.scene.getPlayerPokemon()).toBe(slowking);
|
||||
expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||
expect(game.textInterceptor.logs).toContain(
|
||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||
);
|
||||
});
|
||||
|
||||
it("should succeed without message if called indirectly", async () => {
|
||||
vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.CHILLY_RECEPTION);
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING, SpeciesId.MEOWTH]);
|
||||
|
||||
game.move.select(MoveId.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
const [slowking, meowth] = game.scene.getPlayerParty();
|
||||
|
||||
game.move.select(MoveId.METRONOME);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MEOWTH);
|
||||
expect(game.scene.getPlayerPokemon()).toBe(meowth);
|
||||
expect(slowking.isOnField()).toBe(false);
|
||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||
expect(game.textInterceptor.logs).not.toContain(
|
||||
i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }),
|
||||
);
|
||||
});
|
||||
|
||||
// enemy uses another move and weather doesn't change
|
||||
it("check case - enemy not selecting chilly reception doesn't change weather ", async () => {
|
||||
game.override.battleStyle("single").enemyMoveset([MoveId.CHILLY_RECEPTION, MoveId.TACKLE]).moveset(MoveId.SPLASH);
|
||||
|
||||
// Bugcheck test for enemy AI bug
|
||||
it("check case - enemy not selecting chilly reception doesn't change weather", async () => {
|
||||
game.override.enemyMoveset([MoveId.CHILLY_RECEPTION, MoveId.TACKLE]);
|
||||
await game.classicMode.startBattle([SpeciesId.SLOWKING, SpeciesId.MEOWTH]);
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.move.selectEnemyMove(MoveId.TACKLE);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(undefined);
|
||||
});
|
||||
|
||||
it("enemy trainer - expected behavior ", async () => {
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.startingWave(8)
|
||||
.enemyMoveset(MoveId.CHILLY_RECEPTION)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.moveset([MoveId.SPLASH, MoveId.THUNDERBOLT]);
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.JOLTEON]);
|
||||
const RIVAL_MAGIKARP1 = game.scene.getEnemyPokemon()?.id;
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_MAGIKARP1);
|
||||
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
|
||||
// second chilly reception should still switch out
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
expect(game.scene.getEnemyPokemon()?.id === RIVAL_MAGIKARP1);
|
||||
game.move.select(MoveId.THUNDERBOLT);
|
||||
|
||||
// enemy chilly recep move should fail: it's snowing and no option to switch out
|
||||
// no crashing
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.arena.weather?.weatherType).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
372
test/mystery-encounter/encounters/creeping-fog-encounter.test.ts
Normal file
372
test/mystery-encounter/encounters/creeping-fog-encounter.test.ts
Normal file
@ -0,0 +1,372 @@
|
||||
import type BattleScene from "#app/battle-scene";
|
||||
import { CreepingFogEncounter } from "#app/data/mystery-encounters/encounters/creeping-fog-encounter";
|
||||
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import {
|
||||
runMysteryEncounterToEnd,
|
||||
runSelectMysteryEncounterOption,
|
||||
skipBattleRunMysteryEncounterRewardsPhase,
|
||||
} from "#test/mystery-encounter/encounter-test-utils";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
|
||||
const namespace = "mysteryEncounters/creepingFog";
|
||||
const defaultParty = [SpeciesId.LAPRAS, SpeciesId.GENGAR, SpeciesId.ABRA];
|
||||
const defaultBiome = BiomeId.FOREST;
|
||||
const defaultWave = 51;
|
||||
const enemyPokemonForest50_110 = [
|
||||
SpeciesId.MACHAMP,
|
||||
SpeciesId.GRIMMSNARL,
|
||||
SpeciesId.LYCANROC,
|
||||
SpeciesId.ALOLA_RATICATE,
|
||||
];
|
||||
const enemyPokemonSwamp110_140 = [
|
||||
SpeciesId.MACHAMP,
|
||||
SpeciesId.GRIMMSNARL,
|
||||
SpeciesId.POLIWRATH,
|
||||
SpeciesId.SCOLIPEDE,
|
||||
SpeciesId.MIENSHAO,
|
||||
SpeciesId.DRACOZOLT,
|
||||
];
|
||||
const enemyPokemonGraveyard140_Plus = [
|
||||
SpeciesId.MACHAMP,
|
||||
SpeciesId.GRIMMSNARL,
|
||||
SpeciesId.GOLURK,
|
||||
SpeciesId.HONEDGE,
|
||||
SpeciesId.ZWEILOUS,
|
||||
SpeciesId.SCOLIPEDE,
|
||||
SpeciesId.MIENSHAO,
|
||||
SpeciesId.DRACOZOLT,
|
||||
SpeciesId.PIDGEOT,
|
||||
];
|
||||
|
||||
const enemyMoveset = {
|
||||
[SpeciesId.MACHAMP]: [MoveId.DYNAMIC_PUNCH, MoveId.STONE_EDGE, MoveId.DUAL_CHOP, MoveId.FISSURE],
|
||||
[SpeciesId.GRIMMSNARL]: [MoveId.STONE_EDGE, MoveId.CLOSE_COMBAT, MoveId.IRON_TAIL, MoveId.PLAY_ROUGH],
|
||||
[SpeciesId.LYCANROC]: [MoveId.STONE_EDGE, MoveId.CLOSE_COMBAT, MoveId.IRON_TAIL, MoveId.PLAY_ROUGH],
|
||||
[SpeciesId.ALOLA_RATICATE]: [MoveId.FALSE_SURRENDER, MoveId.SUCKER_PUNCH, MoveId.PLAY_ROUGH, MoveId.POPULATION_BOMB],
|
||||
[SpeciesId.POLIWRATH]: [MoveId.DYNAMIC_PUNCH, MoveId.HYDRO_PUMP, MoveId.DUAL_CHOP, MoveId.HYPNOSIS],
|
||||
[SpeciesId.GOLURK]: [MoveId.EARTHQUAKE, MoveId.POLTERGEIST, MoveId.DYNAMIC_PUNCH, MoveId.STONE_EDGE],
|
||||
[SpeciesId.HONEDGE]: [MoveId.IRON_HEAD, MoveId.POLTERGEIST, MoveId.SACRED_SWORD, MoveId.SHADOW_SNEAK],
|
||||
[SpeciesId.ZWEILOUS]: [MoveId.DRAGON_RUSH, MoveId.CRUNCH, MoveId.GUNK_SHOT, MoveId.SCREECH],
|
||||
[SpeciesId.SCOLIPEDE]: [MoveId.MEGAHORN, MoveId.NOXIOUS_TORQUE, MoveId.ROLLOUT, MoveId.BANEFUL_BUNKER],
|
||||
[SpeciesId.MIENSHAO]: [MoveId.HIGH_JUMP_KICK, MoveId.STONE_EDGE, MoveId.BLAZE_KICK, MoveId.GUNK_SHOT],
|
||||
[SpeciesId.DRACOZOLT]: [MoveId.BOLT_BEAK, MoveId.DRAGON_RUSH, MoveId.EARTHQUAKE, MoveId.STONE_EDGE],
|
||||
[SpeciesId.PIDGEOT]: [MoveId.HURRICANE, MoveId.HEAT_WAVE, MoveId.FOCUS_BLAST, MoveId.WILDBOLT_STORM],
|
||||
};
|
||||
|
||||
describe("Creeping Fog - Mystery Encounter", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
game.override.mysteryEncounterChance(100);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWaves();
|
||||
game.override.startingTimeOfDay(TimeOfDay.NIGHT);
|
||||
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||
new Map<BiomeId, MysteryEncounterType[]>([
|
||||
[BiomeId.FOREST, [MysteryEncounterType.CREEPING_FOG]],
|
||||
[BiomeId.FOREST, [MysteryEncounterType.SAFARI_ZONE]],
|
||||
[BiomeId.SPACE, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
|
||||
expect(CreepingFogEncounter.encounterType).toBe(MysteryEncounterType.CREEPING_FOG);
|
||||
expect(CreepingFogEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA);
|
||||
expect(CreepingFogEncounter.dialogue).toBeDefined();
|
||||
expect(CreepingFogEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||
expect(CreepingFogEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}:title`);
|
||||
expect(CreepingFogEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}:description`);
|
||||
expect(CreepingFogEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}:query`);
|
||||
expect(CreepingFogEncounter.options.length).toBe(4);
|
||||
});
|
||||
|
||||
it("should not spawn outside of proper biomes", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.ULTRA);
|
||||
game.override.startingBiome(BiomeId.SPACE);
|
||||
game.override.startingTimeOfDay(TimeOfDay.NIGHT);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
expect(game.scene.currentBattle.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CREEPING_FOG);
|
||||
});
|
||||
|
||||
it("should not spawn outside of proper time of day", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.ULTRA);
|
||||
game.override.startingTimeOfDay(TimeOfDay.DAY);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
expect(game.scene.currentBattle.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CREEPING_FOG);
|
||||
});
|
||||
|
||||
describe("Option 1 - Confront the shadow", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = CreepingFogEncounter.options[0];
|
||||
expect(option1.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option1.dialogue).toBeDefined();
|
||||
expect(option1.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.1.label`,
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.1.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should start battle against shadowy Pokemon from the Forest Low Level Pool", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
//Expect that the weather is set to heavy fog
|
||||
expect(scene.arena.weather?.weatherType).toBe(WeatherType.HEAVY_FOG);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyPokemonForest50_110).toContain(enemyField[0].species.speciesId);
|
||||
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||
expect(enemyMoveset[enemyField[0].species.speciesId]).toEqual(moveset);
|
||||
});
|
||||
|
||||
it("should start battle against shadowy Pokemon from the Swamp Mid Level Pool", async () => {
|
||||
game.override.startingWave(113);
|
||||
game.override.startingBiome(BiomeId.SWAMP);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
// Make party lead's level arbitrarily high to not get KOed by move
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
//Expect that the weather is set to heavy fog
|
||||
expect(scene.arena.weather?.weatherType).toBe(WeatherType.HEAVY_FOG);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyPokemonSwamp110_140).toContain(enemyField[0].species.speciesId);
|
||||
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||
expect(enemyMoveset[enemyField[0].species.speciesId]).toEqual(moveset);
|
||||
});
|
||||
|
||||
it("should start battle against shadowy Pokemon from the Graveyard High Level Pool", async () => {
|
||||
game.override.startingWave(143);
|
||||
game.override.startingBiome(BiomeId.GRAVEYARD);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
// Make party lead's level arbitrarily high to not get KOed by move
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
//Expect that the weather is set to heavy fog
|
||||
expect(scene.arena.weather?.weatherType).toBe(WeatherType.HEAVY_FOG);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyPokemonGraveyard140_Plus).toContain(enemyField[0].species.speciesId);
|
||||
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||
expect(enemyMoveset[enemyField[0].species.speciesId]).toEqual(moveset);
|
||||
});
|
||||
|
||||
it("should have a 2 rogue tier items in the rewards after battle", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
h => h instanceof ModifierSelectUiHandler,
|
||||
) as ModifierSelectUiHandler;
|
||||
expect(
|
||||
modifierSelectHandler.options[0].modifierTypeOption.type.tier -
|
||||
modifierSelectHandler.options[0].modifierTypeOption.upgradeCount,
|
||||
).toEqual(ModifierTier.ROGUE);
|
||||
expect(
|
||||
modifierSelectHandler.options[1].modifierTypeOption.type.tier -
|
||||
modifierSelectHandler.options[1].modifierTypeOption.upgradeCount,
|
||||
).toEqual(ModifierTier.ROGUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - Clear the Fog", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option2 = CreepingFogEncounter.options[1];
|
||||
expect(option2.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL);
|
||||
expect(option2.dialogue).toBeDefined();
|
||||
expect(option2.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.2.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should skip battle with Pokemon if wave level under 140", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(MoveId.DEFOG)];
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
|
||||
it("should not skip battle with Pokemon", async () => {
|
||||
game.override.startingWave(143);
|
||||
game.override.startingBiome(BiomeId.GRAVEYARD);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(MoveId.DEFOG)];
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyPokemonGraveyard140_Plus).toContain(enemyField[0].species.speciesId);
|
||||
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||
expect(enemyMoveset[enemyField[0].species.speciesId]).toEqual(moveset);
|
||||
});
|
||||
|
||||
it("should NOT be selectable if the player doesn't have a defog type move", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||
await runSelectMysteryEncounterOption(game, 2);
|
||||
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 3 - Navigate through the Fog", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option3 = CreepingFogEncounter.options[2];
|
||||
expect(option3.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL);
|
||||
expect(option3.dialogue).toBeDefined();
|
||||
expect(option3.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.3.label`,
|
||||
buttonTooltip: `${namespace}:option.3.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.3.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should skip battle with Pokemon if wave level under 140", async () => {
|
||||
game.override.startingWave(63);
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(MoveId.FORESIGHT)];
|
||||
await runMysteryEncounterToEnd(game, 3);
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
|
||||
it("should not skip battle with Pokemon", async () => {
|
||||
game.override.startingWave(143);
|
||||
game.override.startingBiome(BiomeId.GRAVEYARD);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(MoveId.FORESIGHT)];
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
//Expect that the weather is set to heavy fog
|
||||
expect(scene.arena.weather?.weatherType).toBe(WeatherType.HEAVY_FOG);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyPokemonGraveyard140_Plus).toContain(enemyField[0].species.speciesId);
|
||||
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||
expect(enemyMoveset[enemyField[0].species.speciesId]).toEqual(moveset);
|
||||
});
|
||||
|
||||
it("should NOT be selectable if the player doesn't have a light type move", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||
|
||||
await runSelectMysteryEncounterOption(game, 3);
|
||||
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 4 - Leave the encounter", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option4 = CreepingFogEncounter.options[3];
|
||||
expect(option4.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option4.dialogue).toBeDefined();
|
||||
expect(option4.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.4.label`,
|
||||
buttonTooltip: `${namespace}:option.4.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.4.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 4);
|
||||
//Expect that the weather is set to fog
|
||||
expect(scene.arena.weather?.weatherType).toBe(WeatherType.FOG);
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -17,6 +17,7 @@ import { GameManagerHelper } from "./gameManagerHelper";
|
||||
import { coerceArray, shiftCharCodes } from "#app/utils/common";
|
||||
import type { RandomTrainerOverride } from "#app/overrides";
|
||||
import type { BattleType } from "#enums/battle-type";
|
||||
import type { TimeOfDay } from "#enums/time-of-day";
|
||||
|
||||
/**
|
||||
* Helper to handle overrides in tests
|
||||
@ -38,6 +39,17 @@ export class OverridesHelper extends GameManagerHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the starting time of day
|
||||
* @param timeOfDay - The time of day to be set
|
||||
* @returns `this`
|
||||
*/
|
||||
public startingTimeOfDay(timeOfDay: TimeOfDay): this {
|
||||
vi.spyOn(Overrides, "ARENA_TINT_OVERRIDE", "get").mockReturnValue(timeOfDay);
|
||||
this.log(`Starting time of day set to ${timeOfDay}!`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the starting wave index
|
||||
* @param wave - The wave to set. Classic: `1`-`200`
|
||||
|
Loading…
Reference in New Issue
Block a user