mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-01 05:52:17 +02:00
Implement Creeping Fog Mystery Encounter Feature
This Mystery Encounter is a random event that can occur from wave 50 and above that causes a fog to appear covering the screen and a wild pokemon to be hidden underneath it. The player when facing this encounter may choose one of four options which includes fighting, using a light based move or ability to light the way, using a defog move or ability to clear the fog, or waiting for the fog to thin out. Depending on your choice the player might be granted more experience and better rewards. The pokemon encountered changes dynamically with the biome and stage level that the Mystery Encounter occurs. Changes made: - Implemented new mystery encounter called "Creeping Fog" ("creeping-fog-encounter.ts") and added a new corresponding weather type called "Heavy Fog" ("weather.ts"). - Updated corresponding weather functions and fog affected pokemon functions. ("move.ts", "pokemon-forms.ts", "ability.ts", "pokemon-evolutions.ts"). - Created new Fog Overlay for the mystery encounter ("fog-overlay.ts"). - Created new item called "Micle Berry" to grant perfect accuracy ("modifier-type.ts"). - Created Mystery Encounter Creeping Fog unit test ("creeping-fog-encounter.test.ts"). - Created new Mystery Encounter Light and Defog moves and abilities requirements ("requirements-groups.ts"). Signed-off-by: Fuad Ali <fuad.ali@tecnico.ulisboa.pt> Co-authored-by: Matilde Simões <matilde.simoes@tecnico.ulisboa.pt>
This commit is contained in:
parent
ff9aefb0e5
commit
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 |
@ -8111,7 +8111,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()
|
||||
@ -8299,7 +8299,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(),
|
||||
@ -9003,7 +9003,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()
|
||||
|
@ -1138,7 +1138,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.SLIGGOO, 40, null, new TimeOfDayEvolutionCondition("day"))
|
||||
],
|
||||
[SpeciesId.SLIGGOO]: [
|
||||
new SpeciesEvolution(SpeciesId.GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
|
||||
new SpeciesEvolution(SpeciesId.GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[SpeciesId.BERGMITE]: [
|
||||
new SpeciesEvolution(SpeciesId.HISUI_AVALUGG, 37, null, new TimeOfDayEvolutionCondition("night")),
|
||||
@ -1361,7 +1361,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.HISUI_ZOROARK, 30, null, null)
|
||||
],
|
||||
[SpeciesId.HISUI_SLIGGOO]: [
|
||||
new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
|
||||
new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[SpeciesId.SPRIGATITO]: [
|
||||
new SpeciesEvolution(SpeciesId.FLORAGATO, 16, null, null)
|
||||
|
@ -768,9 +768,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");
|
||||
|
||||
@ -778,7 +775,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}
|
||||
@ -9365,7 +9366,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;
|
||||
}
|
||||
|
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/modifier/modifier-type";
|
||||
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 { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
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 { Abilities } from "#enums/abilities";
|
||||
import { Stat } from "#enums/stat";
|
||||
import type HeldModifierConfig from "#app/interfaces/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 "#app/modifier/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 { Biome } from "#enums/biome";
|
||||
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 encounter.
|
||||
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/4423 | 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 Species;
|
||||
const naturePokemon = chosenPokemonAttributes[1] as Nature;
|
||||
const abilityPokemon = chosenPokemonAttributes[2] as Abilities;
|
||||
const passivePokemon = chosenPokemon[3] as boolean;
|
||||
const movesPokemon = chosenPokemonAttributes[4] as Moves[];
|
||||
const modifPokemon = chosenPokemonAttributes[5] as HeldModifierConfig[];
|
||||
const segments = waveIndex < 80 ? 2 : waveIndex < 140 ? 3 : 4;
|
||||
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: getPokemonSpecies(chosenPokemon),
|
||||
formIndex: [Species.LYCANROC, Species.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 = [
|
||||
[
|
||||
Species.MACHAMP,
|
||||
Nature.JOLLY,
|
||||
1,
|
||||
false,
|
||||
[Moves.DYNAMIC_PUNCH, Moves.STONE_EDGE, Moves.DUAL_CHOP, Moves.FISSURE],
|
||||
[],
|
||||
],
|
||||
[
|
||||
Species.GRIMMSNARL,
|
||||
Nature.ADAMANT,
|
||||
null,
|
||||
false,
|
||||
[Moves.STONE_EDGE, Moves.CLOSE_COMBAT, Moves.IRON_TAIL, Moves.PLAY_ROUGH],
|
||||
[{ modifier: generateModifierType(modifierTypes.MICLE_BERRY) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
];
|
||||
const ForestTallGrassPokemon = [
|
||||
[
|
||||
Species.LYCANROC,
|
||||
Nature.JOLLY,
|
||||
2,
|
||||
false,
|
||||
[Moves.STONE_EDGE, Moves.CLOSE_COMBAT, Moves.IRON_TAIL, Moves.PLAY_ROUGH],
|
||||
[],
|
||||
],
|
||||
[
|
||||
Species.ALOLA_RATICATE,
|
||||
Nature.ADAMANT,
|
||||
1,
|
||||
false,
|
||||
[Moves.FALSE_SURRENDER, Moves.SUCKER_PUNCH, Moves.PLAY_ROUGH, Moves.POPULATION_BOMB],
|
||||
[{ modifier: generateModifierType(modifierTypes.REVIVER_SEED) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
];
|
||||
const SwampLakePokemon = [
|
||||
[
|
||||
Species.POLIWRATH,
|
||||
Nature.NAIVE,
|
||||
null,
|
||||
true,
|
||||
[Moves.DYNAMIC_PUNCH, Moves.HYDRO_PUMP, Moves.DUAL_CHOP, Moves.HYPNOSIS],
|
||||
[
|
||||
{ modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType },
|
||||
{ modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType },
|
||||
],
|
||||
],
|
||||
];
|
||||
const GraveyardPokemon = [
|
||||
[
|
||||
Species.GOLURK,
|
||||
Nature.ADAMANT,
|
||||
2,
|
||||
false,
|
||||
[Moves.EARTHQUAKE, Moves.POLTERGEIST, Moves.DYNAMIC_PUNCH, Moves.STONE_EDGE],
|
||||
[],
|
||||
],
|
||||
[
|
||||
Species.HONEDGE,
|
||||
Nature.CAREFUL,
|
||||
0,
|
||||
false,
|
||||
[Moves.IRON_HEAD, Moves.POLTERGEIST, Moves.SACRED_SWORD, Moves.SHADOW_SNEAK],
|
||||
[],
|
||||
],
|
||||
[
|
||||
Species.ZWEILOUS,
|
||||
Nature.BRAVE,
|
||||
null,
|
||||
true,
|
||||
[Moves.DRAGON_RUSH, Moves.CRUNCH, Moves.GUNK_SHOT, Moves.SCREECH],
|
||||
[{ modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType, stackCount: 2 }],
|
||||
],
|
||||
];
|
||||
const wave110_140Pokemon = [
|
||||
[
|
||||
Species.SCOLIPEDE,
|
||||
Nature.ADAMANT,
|
||||
2,
|
||||
false,
|
||||
[Moves.MEGAHORN, Moves.NOXIOUS_TORQUE, Moves.ROLLOUT, Moves.BANEFUL_BUNKER],
|
||||
[{ modifier: generateModifierType(modifierTypes.MICLE_BERRY) as PokemonHeldItemModifierType }],
|
||||
],
|
||||
[
|
||||
Species.MIENSHAO,
|
||||
Nature.JOLLY,
|
||||
null,
|
||||
true,
|
||||
[Moves.HIGH_JUMP_KICK, Moves.STONE_EDGE, Moves.BLAZE_KICK, Moves.GUNK_SHOT],
|
||||
[],
|
||||
],
|
||||
[
|
||||
Species.DRACOZOLT,
|
||||
Nature.JOLLY,
|
||||
null,
|
||||
true,
|
||||
[Moves.BOLT_BEAK, Moves.DRAGON_RUSH, Moves.EARTHQUAKE, Moves.STONE_EDGE],
|
||||
[],
|
||||
],
|
||||
];
|
||||
const wave140PlusPokemon = [
|
||||
[
|
||||
Species.PIDGEOT,
|
||||
Nature.HASTY,
|
||||
0,
|
||||
false,
|
||||
[Moves.HURRICANE, Moves.HEAT_WAVE, Moves.FOCUS_BLAST, Moves.WILDBOLT_STORM],
|
||||
[],
|
||||
],
|
||||
];
|
||||
|
||||
let pool = allBiomePokemon as [Species, Nature, Abilities, boolean, Moves[], HeldModifierConfig[]][];
|
||||
|
||||
// Include biome-specific Pokémon if within wave 50-80
|
||||
if (wave >= 50) {
|
||||
if (biome === Biome.FOREST || biome === Biome.TALL_GRASS) {
|
||||
pool = pool.concat(
|
||||
ForestTallGrassPokemon as [Species, Nature, Abilities, boolean, Moves[], HeldModifierConfig[]][],
|
||||
);
|
||||
}
|
||||
if (biome === Biome.SWAMP || biome === Biome.LAKE) {
|
||||
pool = pool.concat(SwampLakePokemon as [Species, Nature, Abilities, boolean, Moves[], HeldModifierConfig[]][]);
|
||||
}
|
||||
if (biome === Biome.GRAVEYARD) {
|
||||
pool = pool.concat(GraveyardPokemon as [Species, Nature, Abilities, boolean, Moves[], HeldModifierConfig[]][]);
|
||||
}
|
||||
}
|
||||
|
||||
// Waves 110-140 content
|
||||
if (wave >= 110) {
|
||||
pool = pool.concat(wave110_140Pokemon as [Species, Nature, Abilities, boolean, Moves[], HeldModifierConfig[]][]);
|
||||
}
|
||||
|
||||
// Wave 140+
|
||||
if (wave >= 140) {
|
||||
pool = pool.concat(wave140PlusPokemon as [Species, Nature, Abilities, boolean, Moves[], 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 = [Moves.DEFOG, Moves.RAPID_SPIN, Moves.GUST];
|
||||
|
||||
/**
|
||||
* Moves that can help navigate through foggy weather
|
||||
*/
|
||||
export const LIGHT_MOVES = [Moves.FLASH, Moves.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 = [Abilities.AIR_LOCK, Abilities.CLOUD_NINE];
|
||||
|
||||
/**
|
||||
* Abilities that can help navigate through foggy weather
|
||||
*/
|
||||
export const LIGHT_ABILITIES = [
|
||||
Abilities.KEEN_EYE,
|
||||
Abilities.ILLUMINATE,
|
||||
Abilities.COMPOUND_EYES,
|
||||
Abilities.VICTORY_STAR,
|
||||
];
|
||||
|
@ -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]: [
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -326,8 +326,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,
|
||||
@ -335,6 +342,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,
|
||||
});
|
||||
@ -2158,6 +2161,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"),
|
||||
|
||||
|
@ -2759,8 +2759,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;
|
||||
}
|
||||
|
||||
|
@ -280,6 +280,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;
|
||||
}
|
||||
}
|
367
test/mystery-encounter/encounters/creeping-fog-encounter.test.ts
Normal file
367
test/mystery-encounter/encounters/creeping-fog-encounter.test.ts
Normal file
@ -0,0 +1,367 @@
|
||||
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 { Biome } from "#app/enums/biome";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { ModifierTier } from "#app/modifier/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 { Moves } from "#enums/moves";
|
||||
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 = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.FOREST;
|
||||
const defaultWave = 51;
|
||||
const enemyPokemonForest50_110 = [Species.MACHAMP, Species.GRIMMSNARL, Species.LYCANROC, Species.ALOLA_RATICATE];
|
||||
const enemyPokemonSwamp110_140 = [
|
||||
Species.MACHAMP,
|
||||
Species.GRIMMSNARL,
|
||||
Species.POLIWRATH,
|
||||
Species.SCOLIPEDE,
|
||||
Species.MIENSHAO,
|
||||
Species.DRACOZOLT,
|
||||
];
|
||||
const enemyPokemonGraveyard140_Plus = [
|
||||
Species.MACHAMP,
|
||||
Species.GRIMMSNARL,
|
||||
Species.GOLURK,
|
||||
Species.HONEDGE,
|
||||
Species.ZWEILOUS,
|
||||
Species.SCOLIPEDE,
|
||||
Species.MIENSHAO,
|
||||
Species.DRACOZOLT,
|
||||
Species.PIDGEOT,
|
||||
];
|
||||
|
||||
const enemyMoveset = {
|
||||
[Species.MACHAMP]: [Moves.DYNAMIC_PUNCH, Moves.STONE_EDGE, Moves.DUAL_CHOP, Moves.FISSURE],
|
||||
[Species.GRIMMSNARL]: [Moves.STONE_EDGE, Moves.CLOSE_COMBAT, Moves.IRON_TAIL, Moves.PLAY_ROUGH],
|
||||
[Species.LYCANROC]: [Moves.STONE_EDGE, Moves.CLOSE_COMBAT, Moves.IRON_TAIL, Moves.PLAY_ROUGH],
|
||||
[Species.ALOLA_RATICATE]: [Moves.FALSE_SURRENDER, Moves.SUCKER_PUNCH, Moves.PLAY_ROUGH, Moves.POPULATION_BOMB],
|
||||
[Species.POLIWRATH]: [Moves.DYNAMIC_PUNCH, Moves.HYDRO_PUMP, Moves.DUAL_CHOP, Moves.HYPNOSIS],
|
||||
[Species.GOLURK]: [Moves.EARTHQUAKE, Moves.POLTERGEIST, Moves.DYNAMIC_PUNCH, Moves.STONE_EDGE],
|
||||
[Species.HONEDGE]: [Moves.IRON_HEAD, Moves.POLTERGEIST, Moves.SACRED_SWORD, Moves.SHADOW_SNEAK],
|
||||
[Species.ZWEILOUS]: [Moves.DRAGON_RUSH, Moves.CRUNCH, Moves.GUNK_SHOT, Moves.SCREECH],
|
||||
[Species.SCOLIPEDE]: [Moves.MEGAHORN, Moves.NOXIOUS_TORQUE, Moves.ROLLOUT, Moves.BANEFUL_BUNKER],
|
||||
[Species.MIENSHAO]: [Moves.HIGH_JUMP_KICK, Moves.STONE_EDGE, Moves.BLAZE_KICK, Moves.GUNK_SHOT],
|
||||
[Species.DRACOZOLT]: [Moves.BOLT_BEAK, Moves.DRAGON_RUSH, Moves.EARTHQUAKE, Moves.STONE_EDGE],
|
||||
[Species.PIDGEOT]: [Moves.HURRICANE, Moves.HEAT_WAVE, Moves.FOCUS_BLAST, Moves.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<Biome, MysteryEncounterType[]>([
|
||||
[Biome.FOREST, [MysteryEncounterType.CREEPING_FOG]],
|
||||
[Biome.FOREST, [MysteryEncounterType.SAFARI_ZONE]],
|
||||
[Biome.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(Biome.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.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(Biome.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.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(Biome.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.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.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(Moves.DEFOG)];
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
|
||||
it("should not skip battle with Pokemon", async () => {
|
||||
game.override.startingWave(143);
|
||||
game.override.startingBiome(Biome.GRAVEYARD);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(Moves.DEFOG)];
|
||||
const partyLead = scene.getPlayerParty()[0];
|
||||
partyLead.level = 1000;
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.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.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(Moves.FORESIGHT)];
|
||||
await runMysteryEncounterToEnd(game, 3);
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
|
||||
it("should not skip battle with Pokemon", async () => {
|
||||
game.override.startingWave(143);
|
||||
game.override.startingBiome(Biome.GRAVEYARD);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CREEPING_FOG, defaultParty);
|
||||
scene.getPlayerParty()[1].moveset = [new PokemonMove(Moves.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.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.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 { 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