[Bug] Fix for Expert Breeder's Pokemon being invisible and IV scanner in safari zone (#4661)

* [Bug] Potential fix for Expert Breeder's Pokemon being invisible

* PR Feedback

* Consistency with await
This commit is contained in:
PigeonBar 2024-10-16 10:31:32 -04:00 committed by GitHub
parent 50ff6e703a
commit c6ec01958c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 120 additions and 47 deletions

View File

@ -789,7 +789,7 @@ export default class BattleScene extends SceneBase {
}
getEnemyParty(): EnemyPokemon[] {
return this.currentBattle?.enemyParty || [];
return this.currentBattle?.enemyParty ?? [];
}
getEnemyPokemon(): EnemyPokemon | undefined {

View File

@ -154,7 +154,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
return initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
}
)
.withSimpleOption(

View File

@ -286,7 +286,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
ignorePp: true
});
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
})
.build()
@ -328,7 +328,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
});
await scene.updateModifiers(true);
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
leaveEncounterWithoutBattle(scene, true);
})
.build()
@ -359,7 +359,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
greedent.passive = true;
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true);
})

View File

@ -228,7 +228,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
})
.withOptionPhase(async (scene: BattleScene) => {
// Learn its Dance
hideOricorioPokemon(scene);
await hideOricorioPokemon(scene);
leaveEncounterWithoutBattle(scene, true);
})
.build()
@ -303,7 +303,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
}
}
hideOricorioPokemon(scene);
await hideOricorioPokemon(scene);
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true);
})

View File

@ -182,7 +182,7 @@ export const DarkDealEncounter: MysteryEncounter =
const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfig ],
};
return initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.build()
)

View File

@ -222,12 +222,13 @@ export const FieryFalloutEncounter: MysteryEncounter =
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Do NOT await this, to prevent player from repeatedly pressing options
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000);
})
.withOptionPhase(async (scene: BattleScene) => {
// Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter!;
transitionMysteryEncounterIntroVisuals(scene);
await transitionMysteryEncounterIntroVisuals(scene);
setEncounterRewards(scene,
{ fillRemaining: true },
undefined,

View File

@ -152,7 +152,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
transitionMysteryEncounterIntroVisuals(scene, true, true);
await transitionMysteryEncounterIntroVisuals(scene, true, true);
leaveEncounterWithoutBattle(scene, true);
return true;
}

View File

@ -399,7 +399,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
if (modifier.stackCount === 0) {
scene.removeModifier(modifier);
}
scene.updateModifiers(true, true);
await scene.updateModifiers(true, true);
// Generate a trainer name
const traderName = generateRandomTraderName();

View File

@ -129,7 +129,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
*
* @param scene Battle scene
*/
async function handlePokemonGuidingYouPhase(scene: BattleScene) {
function handlePokemonGuidingYouPhase(scene: BattleScene) {
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
const { mysteryEncounter } = scene.currentBattle;

View File

@ -147,11 +147,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config);
initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 10);
return ret;
await initBattlePromise!;
}
)
.withSimpleOption(
@ -172,11 +172,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config);
initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 100);
return ret;
await initBattlePromise!;
}
)
.withSimpleOption(
@ -200,11 +200,11 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams
let ret;
let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config);
initBattlePromise = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 1000);
return ret;
await initBattlePromise!;
}
)
.withOutroDialogue([

View File

@ -184,7 +184,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
scene.unshiftPhase(new GameOverPhase(scene));
} else {
// Show which Pokemon was KOed, then start battle against Gimmighoul
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
setEncounterRewards(scene, { fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
}

View File

@ -303,13 +303,22 @@ async function summonSafariPokemon(scene: BattleScene) {
scene.unshiftPhase(new SummonPhase(scene, 0, false));
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared") ?? "", null, 1500, false)
.then(() => {
// TODO: If we await this showEncounterText, then the text will display without
// the wild Pokemon on screen, but if we don't await it, then the text never
// shows up and the IV scanner breaks. For now, we place the IV scanner code
// separately so that at least the IV scanner works.
//
// showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared") ?? "", null, 0, false)
// .then(() => {
// const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
// if (ivScannerModifier) {
// scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
// }
// });
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
if (ivScannerModifier) {
scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
}
});
}
function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {

View File

@ -142,7 +142,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
encounter.setDialogueToken("newNature", getNatureName(newNature));
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo();
await chosenPokemon.updateInfo();
})
.build()
)
@ -204,7 +204,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100);
chosenPokemon.updateInfo();
await chosenPokemon.updateInfo();
})
.build()
)

View File

@ -149,7 +149,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
transitionMysteryEncounterIntroVisuals(scene, true, true);
await transitionMysteryEncounterIntroVisuals(scene, true, true);
await initBattleWithEnemyConfig(scene, config);
}
)

View File

@ -245,7 +245,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}
encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene);
@ -297,7 +297,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}
encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene);
@ -349,7 +349,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}
encounter.onGameOver = onGameOver;
initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.withPostOptionPhase(async (scene: BattleScene) => {
await doPostEncounterCleanup(scene);

View File

@ -201,7 +201,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
});
encounter.dialogue.outro = [];
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
}
)

View File

@ -111,8 +111,8 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
},
async (scene: BattleScene) => {
// Spawn 5 trainer battles back to back with Macho Brace in rewards
scene.currentBattle.mysteryEncounter!.doContinueEncounter = (scene: BattleScene) => {
return endTrainerBattleAndShowDialogue(scene);
scene.currentBattle.mysteryEncounter!.doContinueEncounter = async (scene: BattleScene) => {
await endTrainerBattleAndShowDialogue(scene);
};
await transitionMysteryEncounterIntroVisuals(scene, true, false);
await spawnNextTrainerOrEndEncounter(scene);

View File

@ -162,7 +162,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.build()
)
@ -238,7 +238,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.build()
)
@ -351,7 +351,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
return initBattleWithEnemyConfig(scene, config);
await initBattleWithEnemyConfig(scene, config);
})
.build()
)

View File

@ -105,7 +105,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
})
.withOptionPhase(async (scene: BattleScene) => {
// Gain 2 Leftovers and 2 Shell Bell
transitionMysteryEncounterIntroVisuals(scene);
await transitionMysteryEncounterIntroVisuals(scene);
await tryApplyDigRewardItems(scene);
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
@ -136,7 +136,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
// Investigate garbage, battle Gmax Garbodor
scene.setFieldScale(0.75);
await showEncounterText(scene, `${namespace}:option.2.selected_2`);
transitionMysteryEncounterIntroVisuals(scene);
await transitionMysteryEncounterIntroVisuals(scene);
const encounter = scene.currentBattle.mysteryEncounter!;
@ -222,7 +222,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) {
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true);
}
async function doGarbageDig(scene: BattleScene) {
function doGarbageDig(scene: BattleScene) {
scene.playSound("battle_anims/PRSFX- Dig2");
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
scene.playSound("battle_anims/PRSFX- Dig2");

View File

@ -124,10 +124,31 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
});
});
it("should start battle against the trainer", async () => {
it("should start battle against the trainer with correctly loaded assets", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
let successfullyLoaded = false;
vi.spyOn(scene, "getEnemyParty").mockImplementation(() => {
const ace = scene.currentBattle?.enemyParty[0];
if (ace) {
// Pretend that loading assets takes an extra 500ms
vi.spyOn(ace, "loadAssets").mockImplementation(() => new Promise(resolve => {
setTimeout(() => {
successfullyLoaded = true;
resolve();
}, 500);
}));
}
return scene.currentBattle?.enemyParty ?? [];
});
await runMysteryEncounterToEnd(game, 1, undefined, true);
// Check that assets are successfully loaded
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
@ -182,10 +203,31 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
});
});
it("should start battle against the trainer", async () => {
it("should start battle against the trainer with correctly loaded assets", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
let successfullyLoaded = false;
vi.spyOn(scene, "getEnemyParty").mockImplementation(() => {
const ace = scene.currentBattle?.enemyParty[0];
if (ace) {
// Pretend that loading assets takes an extra 500ms
vi.spyOn(ace, "loadAssets").mockImplementation(() => new Promise(resolve => {
setTimeout(() => {
successfullyLoaded = true;
resolve();
}, 500);
}));
}
return scene.currentBattle?.enemyParty ?? [];
});
await runMysteryEncounterToEnd(game, 2, undefined, true);
// Check that assets are successfully loaded
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
@ -240,10 +282,31 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
});
});
it("should start battle against the trainer", async () => {
it("should start battle against the trainer with correctly loaded assets", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
let successfullyLoaded = false;
vi.spyOn(scene, "getEnemyParty").mockImplementation(() => {
const ace = scene.currentBattle?.enemyParty[0];
if (ace) {
// Pretend that loading assets takes an extra 500ms
vi.spyOn(ace, "loadAssets").mockImplementation(() => new Promise(resolve => {
setTimeout(() => {
successfullyLoaded = true;
resolve();
}, 500);
}));
}
return scene.currentBattle?.enemyParty ?? [];
});
await runMysteryEncounterToEnd(game, 3, undefined, true);
// Check that assets are successfully loaded
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);