[Sprite][Bug][ME] Fix ME Intro visuals for shinies and other shiny related fixes (#4827)
* [ME] Fix GTS Wonder Trade shiny not giving luck * [ME] Shiny Magikarp from Pokemon Salesman can have any variant * [ME] Shiny lock MEs with custom or special sprites * [ME] GTS shows shiny sparkle for received Pokemon * [ME] Shiny lock 'Slumbering Snorlax' and 'The Strong Stuff' * [ME] Dancing Lessson: show shiny sparkle for Oricorio in intro * [ME] Show shiny sparkles for Pokemon in ME intro * fix tests * Ensure shiny sparkle animation is initialized before playing it (Fixes #3924) * make loading variant assets cleaner * cleanup EnemyPokemon shiny initialization * test fixes and final cleanup * Make 'getSpeciesFilterRandomPartyMemberFunc' more readable --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
80555be22c
commit
38d7a26053
|
@ -47,7 +47,7 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
|
|||
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
|
||||
import { SceneBase } from "#app/scene-base";
|
||||
import CandyBar from "#app/ui/candy-bar";
|
||||
import { Variant, variantData } from "#app/data/variant";
|
||||
import { Variant, variantColorCache, variantData, VariantSet } from "#app/data/variant";
|
||||
import { Localizable } from "#app/interfaces/locales";
|
||||
import Overrides from "#app/overrides";
|
||||
import { InputsController } from "#app/inputs-controller";
|
||||
|
@ -345,6 +345,33 @@ export default class BattleScene extends SceneBase {
|
|||
this.load.atlas(key, `images/pokemon/${variant ? "variant/" : ""}${experimental ? "exp/" : ""}${atlasPath}.png`, `images/pokemon/${variant ? "variant/" : ""}${experimental ? "exp/" : ""}${atlasPath}.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
|
||||
*/
|
||||
loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant) {
|
||||
const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey);
|
||||
if (useExpSprite) {
|
||||
fileRoot = `exp/${fileRoot}`;
|
||||
}
|
||||
let variantConfig = variantData;
|
||||
fileRoot.split("/").map(p => variantConfig ? variantConfig = variantConfig[p] : null);
|
||||
const variantSet = variantConfig as VariantSet;
|
||||
if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
|
||||
const populateVariantColors = (key: string): Promise<void> => {
|
||||
return new Promise(resolve => {
|
||||
if (variantColorCache.hasOwnProperty(key)) {
|
||||
return resolve();
|
||||
}
|
||||
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`).then(res => res.json()).then(c => {
|
||||
variantColorCache[key] = c;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
populateVariantColors(spriteKey);
|
||||
}
|
||||
}
|
||||
|
||||
async preload() {
|
||||
if (DEBUG_RNG) {
|
||||
const scene = this;
|
||||
|
@ -891,7 +918,7 @@ export default class BattleScene extends SceneBase {
|
|||
return pokemon;
|
||||
}
|
||||
|
||||
addEnemyPokemon(species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean = false, dataSource?: PokemonData, postProcess?: (enemyPokemon: EnemyPokemon) => void): EnemyPokemon {
|
||||
addEnemyPokemon(species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean = false, shinyLock: boolean = false, dataSource?: PokemonData, postProcess?: (enemyPokemon: EnemyPokemon) => void): EnemyPokemon {
|
||||
if (Overrides.OPP_LEVEL_OVERRIDE > 0) {
|
||||
level = Overrides.OPP_LEVEL_OVERRIDE;
|
||||
}
|
||||
|
@ -901,7 +928,7 @@ export default class BattleScene extends SceneBase {
|
|||
boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1;
|
||||
}
|
||||
|
||||
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource);
|
||||
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, shinyLock, dataSource);
|
||||
if (Overrides.OPP_FUSION_OVERRIDE) {
|
||||
pokemon.generateFusionSpecies();
|
||||
}
|
||||
|
|
|
@ -216,6 +216,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
|||
species: getPokemonSpecies(Species.GREEDENT),
|
||||
isBoss: true,
|
||||
bossSegments: 3,
|
||||
shiny: false, // Shiny lock because of consistency issues between the different options
|
||||
moveSet: [ Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH ],
|
||||
modifierConfigs: bossModifierConfigs,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
|
@ -353,9 +354,9 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
|||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
// Let it have the food
|
||||
// Greedent joins the team, level equal to 2 below highest party member
|
||||
// Greedent joins the team, level equal to 2 below highest party member (shiny locked)
|
||||
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
|
||||
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false);
|
||||
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true);
|
||||
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
|
||||
greedent.passive = true;
|
||||
|
||||
|
|
|
@ -98,7 +98,9 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||
tint: 0.25,
|
||||
x: -5,
|
||||
repeat: true,
|
||||
isPokemon: true
|
||||
isPokemon: true,
|
||||
isShiny: bossPokemon.shiny,
|
||||
variant: bossPokemon.variant
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -92,9 +92,13 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||
.withCatchAllowed(true)
|
||||
.withFleeAllowed(false)
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()!);
|
||||
danceAnim.play(scene);
|
||||
|
||||
const oricorio = scene.getEnemyPokemon()!;
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, scene.getPlayerPokemon()!);
|
||||
danceAnim.play(scene, false, () => {
|
||||
if (oricorio.shiny) {
|
||||
oricorio.sparkle();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
})
|
||||
.withIntroDialogue([
|
||||
|
@ -136,7 +140,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||
}
|
||||
|
||||
const oricorioData = new PokemonData(enemyPokemon);
|
||||
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, oricorioData);
|
||||
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, false, oricorioData);
|
||||
|
||||
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||
|
|
|
@ -114,7 +114,9 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
|||
tint: 0.25,
|
||||
x: -5,
|
||||
repeat: true,
|
||||
isPokemon: true
|
||||
isPokemon: true,
|
||||
isShiny: bossPokemon.shiny,
|
||||
variant: bossPokemon.variant
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -194,10 +194,10 @@ async function summonPlayerPokemon(scene: BattleScene) {
|
|||
playerAnimationPromise = summonPlayerPokemonAnimation(scene, playerPokemon);
|
||||
});
|
||||
|
||||
// Also loads Wobbuffet data
|
||||
// Also loads Wobbuffet data (cannot be shiny)
|
||||
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
|
||||
scene.currentBattle.enemyParty = [];
|
||||
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false);
|
||||
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true);
|
||||
wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ];
|
||||
wobbuffet.setNature(Nature.MILD);
|
||||
wobbuffet.setAlpha(0);
|
||||
|
|
|
@ -12,8 +12,7 @@ import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon
|
|||
import { getTypeRgb } from "#app/data/type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import * as Utils from "#app/utils";
|
||||
import { IntegerHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
|
||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
|
@ -27,6 +26,7 @@ import { trainerNamePools } from "#app/data/trainer-names";
|
|||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
import { doShinySparkleAnim } from "#app/field/anims";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/globalTradeSystem";
|
||||
|
@ -230,7 +230,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
||||
if (!tradePokemon.shiny) {
|
||||
const shinyThreshold = new Utils.IntegerHolder(WONDER_TRADE_SHINY_CHANCE);
|
||||
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
|
||||
if (scene.eventManager.isEventActive()) {
|
||||
shinyThreshold.value *= scene.eventManager.getShinyMultiplier();
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||
const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
|
||||
if (tradePokemon.species.abilityHidden) {
|
||||
if (tradePokemon.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new IntegerHolder(64);
|
||||
const hiddenAbilityChance = new NumberHolder(64);
|
||||
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
|
@ -797,6 +797,14 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
|||
receivedPokeballSprite.x = tradeBaseBg.displayWidth / 2;
|
||||
receivedPokeballSprite.y = tradeBaseBg.displayHeight / 2 - 100;
|
||||
|
||||
// Received pokemon sparkles
|
||||
let pokemonShinySparkle: Phaser.GameObjects.Sprite;
|
||||
if (receivedPokemon.shiny) {
|
||||
pokemonShinySparkle = scene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny");
|
||||
pokemonShinySparkle.setVisible(false);
|
||||
tradeContainer.add(pokemonShinySparkle);
|
||||
}
|
||||
|
||||
const BASE_ANIM_DURATION = 1000;
|
||||
|
||||
// Pokeball falls to the screen
|
||||
|
@ -835,6 +843,11 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
|||
scale: 1,
|
||||
alpha: 0,
|
||||
onComplete: () => {
|
||||
if (receivedPokemon.shiny) {
|
||||
scene.time.delayedCall(500, () => {
|
||||
doShinySparkleAnim(scene, pokemonShinySparkle, receivedPokemon.variant);
|
||||
});
|
||||
}
|
||||
receivedPokeballSprite.destroy();
|
||||
scene.time.delayedCall(2000, () => resolve());
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
|||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: bossSpecies,
|
||||
isBoss: true,
|
||||
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
||||
status: [ StatusEffect.SLEEP, 5 ], // Extra turns on timer for Snorlax's start of fight moves
|
||||
moveSet: [ Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT ],
|
||||
modifierConfigs: [
|
||||
|
|
|
@ -72,13 +72,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
|||
|
||||
let pokemon: PlayerPokemon;
|
||||
if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) {
|
||||
// If no HA mon found or you roll 1%, give shiny Magikarp
|
||||
// If no HA mon found or you roll 1%, give shiny Magikarp with random variant
|
||||
species = getPokemonSpecies(Species.MAGIKARP);
|
||||
const hiddenIndex = species.ability2 ? 2 : 1;
|
||||
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, undefined, true, 0);
|
||||
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex, undefined, true);
|
||||
} else {
|
||||
const hiddenIndex = species.ability2 ? 2 : 1;
|
||||
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex);
|
||||
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex);
|
||||
}
|
||||
pokemon.generateAndPopulateMoveset();
|
||||
|
||||
|
@ -88,7 +86,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
|||
fileRoot: fileRoot,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
isPokemon: true
|
||||
isPokemon: true,
|
||||
isShiny: pokemon.shiny,
|
||||
variant: pokemon.variant
|
||||
});
|
||||
|
||||
const starterTier = speciesStarterCosts[species.speciesId];
|
||||
|
|
|
@ -79,6 +79,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||
species: getPokemonSpecies(Species.SHUCKLE),
|
||||
isBoss: true,
|
||||
bossSegments: 5,
|
||||
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
||||
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ],
|
||||
|
|
|
@ -61,11 +61,12 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
|||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
// Calculate boss mon (shiny locked)
|
||||
const bossSpecies = getPokemonSpecies(Species.GARBODOR);
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: bossSpecies,
|
||||
isBoss: true,
|
||||
shiny: false, // Shiny lock because of custom intro sprite
|
||||
formIndex: 1, // Gmax
|
||||
bossSegmentModifier: 1, // +1 Segment from normal
|
||||
moveSet: [ Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH ]
|
||||
|
|
|
@ -100,7 +100,9 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
|||
hasShadow: true,
|
||||
x: -5,
|
||||
repeat: true,
|
||||
isPokemon: true
|
||||
isPokemon: true,
|
||||
isShiny: pokemon.shiny,
|
||||
variant: pokemon.variant
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -113,13 +115,15 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
|||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const pokemonSprite = encounter.introVisuals!.getSprites();
|
||||
|
||||
scene.tweens.add({ // Bounce at the end
|
||||
// Bounce at the end, then shiny sparkle if the Pokemon is shiny
|
||||
scene.tweens.add({
|
||||
targets: pokemonSprite,
|
||||
duration: 300,
|
||||
ease: "Cubic.easeOut",
|
||||
yoyo: true,
|
||||
y: "-=20",
|
||||
loop: 1,
|
||||
onComplete: () => encounter.introVisuals?.playShinySparkles()
|
||||
});
|
||||
|
||||
scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2"));
|
||||
|
|
|
@ -184,7 +184,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||
dataSource = config.dataSource;
|
||||
enemySpecies = config.species;
|
||||
isBoss = config.isBoss;
|
||||
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.TRAINER, isBoss, dataSource);
|
||||
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.TRAINER, isBoss, false, dataSource);
|
||||
} else {
|
||||
battle.enemyParty[e] = battle.trainer.genPartyMember(e);
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||
enemySpecies = scene.randomSpecies(battle.waveIndex, level, true);
|
||||
}
|
||||
|
||||
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, isBoss, dataSource);
|
||||
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, isBoss, false, dataSource);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import { EvolutionLevel, SpeciesWildEvolutionDelay, pokemonEvolutions, pokemonPr
|
|||
import { Type } from "#enums/type";
|
||||
import { LevelMoves, pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Variant, VariantSet, variantColorCache, variantData } from "#app/data/variant";
|
||||
import { Variant, VariantSet, variantData } from "#app/data/variant";
|
||||
import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
|
||||
|
@ -511,29 +511,8 @@ export abstract class PokemonSpeciesForm {
|
|||
} else {
|
||||
scene.anims.get(spriteKey).frameRate = 10;
|
||||
}
|
||||
let spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
|
||||
const useExpSprite = scene.experimentalSprites && scene.hasExpSprite(spriteKey);
|
||||
if (useExpSprite) {
|
||||
spritePath = `exp/${spritePath}`;
|
||||
}
|
||||
let config = variantData;
|
||||
spritePath.split("/").map(p => config ? config = config[p] : null);
|
||||
const variantSet = config as VariantSet;
|
||||
if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
|
||||
const populateVariantColors = (key: string): Promise<void> => {
|
||||
return new Promise(resolve => {
|
||||
if (variantColorCache.hasOwnProperty(key)) {
|
||||
return resolve();
|
||||
}
|
||||
scene.cachedFetch(`./images/pokemon/variant/${spritePath}.json`).then(res => res.json()).then(c => {
|
||||
variantColorCache[key] = c;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
populateVariantColors(spriteKey).then(() => resolve());
|
||||
return;
|
||||
}
|
||||
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
|
||||
scene.loadPokemonVariantAssets(spriteKey, spritePath, variant);
|
||||
resolve();
|
||||
});
|
||||
if (startLoad) {
|
||||
|
|
|
@ -1173,16 +1173,28 @@ export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: Tr
|
|||
if (!ignoreEvolution) {
|
||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
||||
}
|
||||
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, undefined, postProcess);
|
||||
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, false, undefined, postProcess);
|
||||
};
|
||||
}
|
||||
|
||||
function getSpeciesFilterRandomPartyMemberFunc(speciesFilter: PokemonSpeciesFilter, trainerSlot: TrainerSlot = TrainerSlot.TRAINER, allowLegendaries?: boolean, postProcess?: (EnemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
|
||||
const originalSpeciesFilter = speciesFilter;
|
||||
speciesFilter = (species: PokemonSpecies) => (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) && !species.isTrainerForbidden() && originalSpeciesFilter(species);
|
||||
return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => {
|
||||
const ret = scene.addEnemyPokemon(getPokemonSpecies(scene.randomSpecies(scene.currentBattle.waveIndex, level, false, speciesFilter).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex)), level, trainerSlot, undefined, undefined, postProcess);
|
||||
return ret;
|
||||
function getSpeciesFilterRandomPartyMemberFunc(
|
||||
originalSpeciesFilter: PokemonSpeciesFilter,
|
||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||
allowLegendaries?: boolean,
|
||||
postProcess?: (EnemyPokemon: EnemyPokemon) => void
|
||||
): PartyMemberFunc {
|
||||
|
||||
const speciesFilter = (species: PokemonSpecies): boolean => {
|
||||
const notLegendary = !species.legendary && !species.subLegendary && !species.mythical;
|
||||
return (allowLegendaries || notLegendary) && !species.isTrainerForbidden() && originalSpeciesFilter(species);
|
||||
};
|
||||
|
||||
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
const species = getPokemonSpecies(scene.randomSpecies(waveIndex, level, false, speciesFilter)
|
||||
.getTrainerSpeciesForLevel(level, true, strength, waveIndex));
|
||||
|
||||
return scene.addEnemyPokemon(species, level, trainerSlot, undefined, false, undefined, postProcess);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import * as Utils from "../utils";
|
||||
import { Variant } from "#app/data/variant";
|
||||
import { getFrameMs, randGauss } from "#app/utils";
|
||||
|
||||
export function addPokeballOpenParticles(scene: BattleScene, x: number, y: number, pokeballType: PokeballType): void {
|
||||
switch (pokeballType) {
|
||||
|
@ -127,7 +128,7 @@ function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y:
|
|||
|
||||
const particleTimer = scene.tweens.addCounter({
|
||||
repeat: -1,
|
||||
duration: Utils.getFrameMs(1),
|
||||
duration: getFrameMs(1),
|
||||
onRepeat: () => {
|
||||
updateParticle();
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ export function addPokeballCaptureStars(scene: BattleScene, pokeball: Phaser.Gam
|
|||
}
|
||||
});
|
||||
|
||||
const dist = Utils.randGauss(25);
|
||||
const dist = randGauss(25);
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
x: pokeball.x + dist,
|
||||
|
@ -185,3 +186,31 @@ export function sin(index: integer, amplitude: integer): number {
|
|||
export function cos(index: integer, amplitude: integer): number {
|
||||
return amplitude * Math.cos(index * (Math.PI / 128));
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the shiny sparkle animation and sound effect for the given sprite
|
||||
* First ensures that the animation has been properly initialized
|
||||
* @param sparkleSprite the Sprite to play the animation on
|
||||
* @param variant which shiny {@linkcode variant} to play the animation for
|
||||
*/
|
||||
export function doShinySparkleAnim(scene: BattleScene, sparkleSprite: Phaser.GameObjects.Sprite, variant: Variant) {
|
||||
const keySuffix = variant ? `_${variant + 1}` : "";
|
||||
const spriteKey = `shiny${keySuffix}`;
|
||||
const animationKey = `sparkle${keySuffix}`;
|
||||
|
||||
// Make sure the animation exists, and create it if not
|
||||
if (!scene.anims.exists(animationKey)) {
|
||||
const frameNames = scene.anims.generateFrameNames(spriteKey, { suffix: ".png", end: 34 });
|
||||
scene.anims.create({
|
||||
key: `sparkle${keySuffix}`,
|
||||
frames: frameNames,
|
||||
frameRate: 32,
|
||||
showOnStart: true,
|
||||
hideOnComplete: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Play the animation
|
||||
sparkleSprite.play(animationKey);
|
||||
scene.playSound("se/sparkle");
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { GameObjects } from "phaser";
|
||||
import BattleScene from "../battle-scene";
|
||||
import MysteryEncounter from "../data/mystery-encounters/mystery-encounter";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { Species } from "#enums/species";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig;
|
||||
import { Variant } from "#app/data/variant";
|
||||
import { doShinySparkleAnim } from "#app/field/anims";
|
||||
|
||||
type KnownFileRoot =
|
||||
| "arenas"
|
||||
|
@ -59,6 +61,10 @@ export class MysteryEncounterSpriteConfig {
|
|||
scale?: number;
|
||||
/** If you are using a Pokemon sprite, set to `true`. This will ensure variant, form, gender, shiny sprites are loaded properly */
|
||||
isPokemon?: boolean;
|
||||
/** If using a Pokemon shiny sprite, needs to be set to ensure the correct variant assets get loaded and displayed */
|
||||
isShiny?: boolean;
|
||||
/** If using a Pokemon shiny sprite, needs to be set to ensure the correct variant assets get loaded and displayed */
|
||||
variant?: Variant;
|
||||
/** If you are using an item sprite, set to `true` */
|
||||
isItem?: boolean;
|
||||
/** The sprites alpha. `0` - `1` The lower the number, the more transparent */
|
||||
|
@ -74,6 +80,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
public encounter: MysteryEncounter;
|
||||
public spriteConfigs: MysteryEncounterSpriteConfig[];
|
||||
public enterFromRight: boolean;
|
||||
private shinySparkleSprites: { sprite: Phaser.GameObjects.Sprite, variant: Variant }[];
|
||||
|
||||
constructor(scene: BattleScene, encounter: MysteryEncounter) {
|
||||
super(scene, -72, 76);
|
||||
|
@ -86,7 +93,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
};
|
||||
|
||||
if (!isNullOrUndefined(result.species)) {
|
||||
const keys = getSpriteKeysFromSpecies(result.species);
|
||||
const keys = getSpriteKeysFromSpecies(result.species, undefined, undefined, result.isShiny, result.variant);
|
||||
result.spriteKey = keys.spriteKey;
|
||||
result.fileRoot = keys.fileRoot;
|
||||
result.isPokemon = true;
|
||||
|
@ -120,18 +127,36 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
// Sprites with custom X or Y defined will not count for normal spacing requirements
|
||||
const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1));
|
||||
|
||||
this.shinySparkleSprites = [];
|
||||
const shinySparkleSprites = scene.add.container(0, 0);
|
||||
this.spriteConfigs?.forEach((config) => {
|
||||
const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha } = config;
|
||||
const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha, isPokemon, isShiny, variant } = config;
|
||||
|
||||
let sprite: GameObjects.Sprite;
|
||||
let tintSprite: GameObjects.Sprite;
|
||||
let pokemonShinySparkle: Phaser.GameObjects.Sprite | undefined;
|
||||
|
||||
if (!isItem) {
|
||||
sprite = getSprite(spriteKey, hasShadow, yShadow);
|
||||
tintSprite = getSprite(spriteKey);
|
||||
} else {
|
||||
if (isItem) {
|
||||
sprite = getItemSprite(spriteKey, hasShadow, yShadow);
|
||||
tintSprite = getItemSprite(spriteKey);
|
||||
} else {
|
||||
sprite = getSprite(spriteKey, hasShadow, yShadow);
|
||||
tintSprite = getSprite(spriteKey);
|
||||
if (isPokemon && isShiny) {
|
||||
// Set Pipeline for shiny variant
|
||||
sprite.setPipelineData("spriteKey", spriteKey);
|
||||
tintSprite.setPipelineData("spriteKey", spriteKey);
|
||||
sprite.setPipelineData("shiny", true);
|
||||
sprite.setPipelineData("variant", variant);
|
||||
tintSprite.setPipelineData("shiny", true);
|
||||
tintSprite.setPipelineData("variant", variant);
|
||||
// Create Sprite for shiny Sparkle
|
||||
pokemonShinySparkle = scene.add.sprite(sprite.x, sprite.y, "shiny");
|
||||
pokemonShinySparkle.setOrigin(0.5, 1);
|
||||
pokemonShinySparkle.setVisible(false);
|
||||
this.shinySparkleSprites.push({ sprite: pokemonShinySparkle, variant: variant ?? 0 });
|
||||
shinySparkleSprites.add(pokemonShinySparkle);
|
||||
}
|
||||
}
|
||||
|
||||
sprite.setVisible(!config.hidden);
|
||||
|
@ -165,6 +190,11 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
}
|
||||
}
|
||||
|
||||
if (!isNullOrUndefined(pokemonShinySparkle)) {
|
||||
// Offset the sparkle to match the Pokemon's position
|
||||
pokemonShinySparkle.setPosition(sprite.x, sprite.y);
|
||||
}
|
||||
|
||||
if (!isNullOrUndefined(alpha)) {
|
||||
sprite.setAlpha(alpha);
|
||||
tintSprite.setAlpha(alpha);
|
||||
|
@ -173,6 +203,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
this.add(sprite);
|
||||
this.add(tintSprite);
|
||||
});
|
||||
this.add(shinySparkleSprites);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,6 +218,9 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
this.spriteConfigs.forEach((config) => {
|
||||
if (config.isPokemon) {
|
||||
this.scene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
|
||||
if (config.isShiny) {
|
||||
this.scene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant);
|
||||
}
|
||||
} else if (config.isItem) {
|
||||
this.scene.loadAtlas("items", "");
|
||||
} else {
|
||||
|
@ -240,11 +274,21 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
this.getSprites().map((sprite, i) => {
|
||||
if (!this.spriteConfigs[i].isItem) {
|
||||
sprite.setTexture(this.spriteConfigs[i].spriteKey).setFrame(0);
|
||||
if (sprite.texture.frameTotal > 1) {
|
||||
// Show the first animation frame for a smooth transition when the animation starts.
|
||||
const firstFrame = sprite.texture.frames["0001.png"];
|
||||
sprite.setFrame(firstFrame ?? 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.getTintSprites().map((tintSprite, i) => {
|
||||
if (!this.spriteConfigs[i].isItem) {
|
||||
tintSprite.setTexture(this.spriteConfigs[i].spriteKey).setFrame(0);
|
||||
if (tintSprite.texture.frameTotal > 1) {
|
||||
// Show the first frame for a smooth transition when the animation starts.
|
||||
const firstFrame = tintSprite.texture.frames["0001.png"];
|
||||
tintSprite.setFrame(firstFrame ?? 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -288,6 +332,17 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play shiny sparkle animations if there are shiny Pokemon
|
||||
*/
|
||||
playShinySparkles() {
|
||||
for (const sparkleConfig of this.shinySparkleSprites) {
|
||||
this.scene.time.delayedCall(500, () => {
|
||||
doShinySparkleAnim(this.scene, sparkleConfig.sprite, sparkleConfig.variant);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For sprites with animation and that do not have animation disabled, will begin frame animation
|
||||
*/
|
||||
|
|
|
@ -69,6 +69,7 @@ import { SpeciesFormKey } from "#enums/species-form-key";
|
|||
import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE, SHINY_EPIC_CHANCE, SHINY_VARIANT_CHANCE } from "#app/data/balance/rates";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { doShinySparkleAnim } from "#app/field/anims";
|
||||
|
||||
export enum FieldPosition {
|
||||
CENTER,
|
||||
|
@ -673,21 +674,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
initShinySparkle(): void {
|
||||
const keySuffix = this.variant ? `_${this.variant + 1}` : "";
|
||||
const key = `shiny${keySuffix}`;
|
||||
const shinySparkle = this.scene.addFieldSprite(0, 0, key);
|
||||
const shinySparkle = this.scene.addFieldSprite(0, 0, "shiny");
|
||||
shinySparkle.setVisible(false);
|
||||
shinySparkle.setOrigin(0.5, 1);
|
||||
const frameNames = this.scene.anims.generateFrameNames(key, { suffix: ".png", end: 34 });
|
||||
if (!(this.scene.anims.exists(`sparkle${keySuffix}`))) {
|
||||
this.scene.anims.create({
|
||||
key: `sparkle${keySuffix}`,
|
||||
frames: frameNames,
|
||||
frameRate: 32,
|
||||
showOnStart: true,
|
||||
hideOnComplete: true,
|
||||
});
|
||||
}
|
||||
this.add(shinySparkle);
|
||||
|
||||
this.shinySparkle = shinySparkle;
|
||||
|
@ -1976,6 +1965,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
/**
|
||||
* Function that tries to set a Pokemon shiny based on seed.
|
||||
* For manual use only, usually to roll a Pokemon's shiny chance a second time.
|
||||
* If it rolls shiny, also sets a random variant and give the Pokemon the associated luck.
|
||||
*
|
||||
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536`
|
||||
* @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
||||
|
@ -2001,6 +1991,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.shiny = randSeedInt(65536) < shinyThreshold.value;
|
||||
|
||||
if (this.shiny) {
|
||||
this.variant = this.generateShinyVariant();
|
||||
this.luck = this.variant + 1 + (this.fusionShiny ? this.fusionVariant + 1 : 0);
|
||||
this.initShinySparkle();
|
||||
}
|
||||
|
||||
|
@ -3802,8 +3794,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
sparkle(): void {
|
||||
if (this.shinySparkle) {
|
||||
this.shinySparkle.play(`sparkle${this.variant ? `_${this.variant + 1}` : ""}`);
|
||||
this.scene.playSound("se/sparkle");
|
||||
doShinySparkleAnim(this.scene, this.shinySparkle, this.variant);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4646,12 +4637,13 @@ export class EnemyPokemon extends Pokemon {
|
|||
public aiType: AiType;
|
||||
public bossSegments: integer;
|
||||
public bossSegmentIndex: integer;
|
||||
/** To indicate of the instance was populated with a dataSource -> e.g. loaded & populated from session data */
|
||||
/** To indicate if the instance was populated with a dataSource -> e.g. loaded & populated from session data */
|
||||
public readonly isPopulatedFromDataSource: boolean;
|
||||
|
||||
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource?: PokemonData) {
|
||||
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex,
|
||||
dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, undefined, dataSource ? dataSource.nature : undefined, dataSource);
|
||||
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, shinyLock: boolean = false, dataSource?: PokemonData) {
|
||||
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex, dataSource?.gender,
|
||||
(!shinyLock && dataSource) ? dataSource.shiny : false, (!shinyLock && dataSource) ? dataSource.variant : undefined,
|
||||
undefined, dataSource ? dataSource.nature : undefined, dataSource);
|
||||
|
||||
this.trainerSlot = trainerSlot;
|
||||
this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource
|
||||
|
@ -4680,12 +4672,15 @@ export class EnemyPokemon extends Pokemon {
|
|||
if (!dataSource) {
|
||||
this.generateAndPopulateMoveset();
|
||||
|
||||
this.trySetShiny();
|
||||
if (Overrides.OPP_SHINY_OVERRIDE) {
|
||||
if (shinyLock || Overrides.OPP_SHINY_OVERRIDE === false) {
|
||||
this.shiny = false;
|
||||
} else {
|
||||
this.trySetShiny();
|
||||
}
|
||||
|
||||
if (!this.shiny && Overrides.OPP_SHINY_OVERRIDE) {
|
||||
this.shiny = true;
|
||||
this.initShinySparkle();
|
||||
} else if (Overrides.OPP_SHINY_OVERRIDE === false) {
|
||||
this.shiny = false;
|
||||
}
|
||||
|
||||
if (this.shiny) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
|||
import * as Utils from "#app/utils";
|
||||
import { EggLapsePhase } from "./egg-lapse-phase";
|
||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||
import { doShinySparkleAnim } from "#app/field/anims";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -341,8 +342,7 @@ export class EggHatchPhase extends Phase {
|
|||
this.pokemon.cry();
|
||||
if (isShiny) {
|
||||
this.scene.time.delayedCall(Utils.fixedInt(500), () => {
|
||||
this.pokemonShinySparkle.play(`sparkle${this.pokemon.variant ? `_${this.pokemon.variant + 1}` : ""}`);
|
||||
this.scene.playSound("se/sparkle");
|
||||
doShinySparkleAnim(this.scene, this.pokemonShinySparkle, this.pokemon.variant);
|
||||
});
|
||||
}
|
||||
this.scene.time.delayedCall(Utils.fixedInt(!this.skipped ? !isShiny ? 1250 : 1750 : !isShiny ? 250 : 750), () => {
|
||||
|
|
|
@ -379,6 +379,9 @@ export class EncounterPhase extends BattlePhase {
|
|||
|
||||
if (encounter.onVisualsStart) {
|
||||
encounter.onVisualsStart(this.scene);
|
||||
} else if (encounter.spriteConfigs && introVisuals) {
|
||||
// If the encounter doesn't have any special visual intro, show sparkle for shiny Pokemon
|
||||
introVisuals.playShinySparkles();
|
||||
}
|
||||
|
||||
const doEncounter = () => {
|
||||
|
|
|
@ -171,7 +171,7 @@ export default class PokemonData {
|
|||
playerPokemon.nickname = this.nickname;
|
||||
}
|
||||
})
|
||||
: scene.addEnemyPokemon(species, this.level, battleType === BattleType.TRAINER ? !double || !(partyMemberIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER : TrainerSlot.NONE, this.boss, this);
|
||||
: scene.addEnemyPokemon(species, this.level, battleType === BattleType.TRAINER ? !double || !(partyMemberIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER : TrainerSlot.NONE, this.boss, false, this);
|
||||
if (this.summonData) {
|
||||
ret.primeSummonData(this.summonData);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
|||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import * as Utils from "#app/utils";
|
||||
|
||||
const namespace = "mysteryEncounters/globalTradeSystem";
|
||||
const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ];
|
||||
|
@ -176,6 +177,23 @@ describe("Global Trade System - Mystery Encounter", () => {
|
|||
expect(defaultParty.includes(speciesAfter!)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("Should roll for shiny twice, with random variant and associated luck", async () => {
|
||||
// This ensures that the first shiny roll gets ignored, to test the ME rerolling for shiny
|
||||
game.override.enemyShiny(false);
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||
|
||||
vi.spyOn(Utils, "randSeedInt").mockReturnValue(1); // force shiny on reroll
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 });
|
||||
|
||||
const receivedPokemon = scene.getPlayerParty().at(-1)!;
|
||||
|
||||
expect(receivedPokemon.shiny).toBeTruthy();
|
||||
expect(receivedPokemon.variant).toBeDefined();
|
||||
expect(receivedPokemon.luck).toBe(receivedPokemon.variant + 1);
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Should update the player's money properly", async () => {
|
||||
it("should update the player's money properly", async () => {
|
||||
const initialMoney = 20000;
|
||||
scene.money = initialMoney;
|
||||
const updateMoneySpy = vi.spyOn(EncounterPhaseUtils, "updatePlayerMoney");
|
||||
|
@ -137,7 +137,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||
expect(scene.money).toBe(initialMoney - price);
|
||||
});
|
||||
|
||||
it("Should add the Pokemon to the party", async () => {
|
||||
it("should add the Pokemon to the party", async () => {
|
||||
scene.money = 20000;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN, defaultParty);
|
||||
|
||||
|
@ -153,6 +153,18 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||
expect(newlyPurchasedPokemon!.moveset.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should give the purchased Pokemon its HA or make it shiny", async () => {
|
||||
scene.money = 20000;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1);
|
||||
|
||||
const newlyPurchasedPokemon = scene.getPlayerParty()[scene.getPlayerParty().length - 1];
|
||||
const isshiny = newlyPurchasedPokemon.shiny;
|
||||
const hasHA = newlyPurchasedPokemon.abilityIndex === 2;
|
||||
expect(isshiny || hasHA).toBeTruthy();
|
||||
expect(isshiny && hasHA).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should be disabled if player does not have enough money", async () => {
|
||||
scene.money = 0;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN, defaultParty);
|
||||
|
|
|
@ -109,6 +109,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||
species: getPokemonSpecies(Species.SHUCKLE),
|
||||
isBoss: true,
|
||||
bossSegments: 5,
|
||||
shiny: false,
|
||||
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ],
|
||||
|
|
|
@ -92,6 +92,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||
{
|
||||
species: getPokemonSpecies(Species.GARBODOR),
|
||||
isBoss: true,
|
||||
shiny: false,
|
||||
formIndex: 1,
|
||||
bossSegmentModifier: 1,
|
||||
moveSet: [ Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH ],
|
||||
|
|
Loading…
Reference in New Issue