commit latest test changes

This commit is contained in:
ImperialSympathizer 2024-07-19 12:00:03 -04:00
parent 2fce6a2769
commit 37419eb1d3
21 changed files with 269 additions and 212 deletions

View File

@ -206,7 +206,7 @@ export default class Battle {
getBgmOverride(scene: BattleScene): string { getBgmOverride(scene: BattleScene): string {
const battlers = this.enemyParty.slice(0, this.getBattlerCount()); const battlers = this.enemyParty.slice(0, this.getBattlerCount());
if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) { if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) { if (!this.started && this.trainer?.config?.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer.getEncounterBgm()}`; return `encounter_${this.trainer.getEncounterBgm()}`;
} }
if (scene.musicPreference === 0) { if (scene.musicPreference === 0) {

View File

@ -71,7 +71,7 @@ const excludedBosses = [
/** /**
* Dark Deal encounter. * Dark Deal encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/61 | GitHub Issue #61} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/61 | GitHub Issue #61}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const DarkDealEncounter: IMysteryEncounter = export const DarkDealEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)

View File

@ -18,7 +18,7 @@ const namespace = "mysteryEncounter:departmentStoreSale";
/** /**
* Department Store Sale encounter. * Department Store Sale encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/33 | GitHub Issue #33} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/33 | GitHub Issue #33}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const DepartmentStoreSaleEncounter: IMysteryEncounter = export const DepartmentStoreSaleEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)

View File

@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:fieldTrip";
/** /**
* Field Trip encounter. * Field Trip encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/17 | GitHub Issue #17} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/17 | GitHub Issue #17}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const FieldTripEncounter: IMysteryEncounter = export const FieldTripEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)

View File

@ -31,7 +31,7 @@ const DAMAGE_PERCENTAGE: number = 20;
/** /**
* Fiery Fallout encounter. * Fiery Fallout encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const FieryFalloutEncounter: IMysteryEncounter = export const FieryFalloutEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT)
@ -127,8 +127,13 @@ export const FieryFalloutEncounter: IMysteryEncounter =
async (scene: BattleScene) => { async (scene: BattleScene) => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter; const encounter = scene.currentBattle.mysteryEncounter;
const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); setEncounterRewards(scene,
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); { fillRemaining: true },
null,
() => {
giveLeadPokemonCharcoal(scene);
});
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -196,8 +201,8 @@ export const FieryFalloutEncounter: IMysteryEncounter =
.withOption( .withOption(
new MysteryEncounterOptionBuilder() new MysteryEncounterOptionBuilder()
.withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically .withPrimaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3PrimaryName dialogue token automatically
.withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically .withSecondaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3SecondaryName dialogue token automatically
.withDialogue({ .withDialogue({
buttonLabel: `${namespace}:option:3:label`, buttonLabel: `${namespace}:option:3:label`,
buttonTooltip: `${namespace}:option:3:tooltip`, buttonTooltip: `${namespace}:option:3:tooltip`,
@ -215,8 +220,12 @@ export const FieryFalloutEncounter: IMysteryEncounter =
// Fire types help calm the Volcarona // Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter; const encounter = scene.currentBattle.mysteryEncounter;
transitionMysteryEncounterIntroVisuals(scene); transitionMysteryEncounterIntroVisuals(scene);
const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); setEncounterRewards(scene,
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); { fillRemaining: true },
null,
() => {
giveLeadPokemonCharcoal(scene);
});
const primary = encounter.options[2].primaryPokemon; const primary = encounter.options[2].primaryPokemon;
const secondary = encounter.options[2].secondaryPokemon[0]; const secondary = encounter.options[2].secondaryPokemon[0];
@ -227,3 +236,15 @@ export const FieryFalloutEncounter: IMysteryEncounter =
.build() .build()
) )
.build(); .build();
function giveLeadPokemonCharcoal(scene: BattleScene) {
// Give first party pokemon Charcoal for free at end of battle
const leadPokemon = scene.getParty()?.[0];
if (leadPokemon) {
const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]);
scene.addModifier(charcoal.type.newModifier(leadPokemon), true);
scene.updateModifiers();
scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.name);
queueEncounterMessage(scene, `${namespace}:found_charcoal`);
}
}

View File

@ -35,7 +35,7 @@ const namespace = "mysteryEncounter:fightOrFlight";
/** /**
* Fight or Flight encounter. * Fight or Flight encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const FightOrFlightEncounter: IMysteryEncounter = export const FightOrFlightEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType( MysteryEncounterBuilder.withEncounterType(

View File

@ -21,7 +21,7 @@ const namespace = "mysteryEncounter:lostAtSea";
/** /**
* Lost at sea encounter. * Lost at sea encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
.withEncounterTier(MysteryEncounterTier.COMMON) .withEncounterTier(MysteryEncounterTier.COMMON)

View File

@ -26,7 +26,7 @@ const namespace = "mysteryEncounter:mysteriousChallengers";
/** /**
* Mysterious Challengers encounter. * Mysterious Challengers encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/41 | GitHub Issue #41} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/41 | GitHub Issue #41}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const MysteriousChallengersEncounter: IMysteryEncounter = export const MysteriousChallengersEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType( MysteryEncounterBuilder.withEncounterType(

View File

@ -14,7 +14,7 @@ const namespace = "mysteryEncounter:mysteriousChest";
/** /**
* Mysterious Chest encounter. * Mysterious Chest encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const MysteriousChestEncounter: IMysteryEncounter = export const MysteriousChestEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST)

View File

@ -24,7 +24,7 @@ const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768];
/** /**
* Safari Zone encounter. * Safari Zone encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const SafariZoneEncounter: IMysteryEncounter = export const SafariZoneEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE)

View File

@ -17,7 +17,7 @@ const namespace = "mysteryEncounter:shadyVitaminDealer";
/** /**
* Shady Vitamin Dealer encounter. * Shady Vitamin Dealer encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/34 | GitHub Issue #34} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/34 | GitHub Issue #34}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const ShadyVitaminDealerEncounter: IMysteryEncounter = export const ShadyVitaminDealerEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType( MysteryEncounterBuilder.withEncounterType(

View File

@ -21,7 +21,7 @@ const namespace = "mysteryEncounter:slumberingSnorlax";
/** /**
* Sleeping Snorlax encounter. * Sleeping Snorlax encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const SlumberingSnorlaxEncounter: IMysteryEncounter = export const SlumberingSnorlaxEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType( MysteryEncounterBuilder.withEncounterType(

View File

@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:trainingSession";
/** /**
* Training Session encounter. * Training Session encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/43 | GitHub Issue #43} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/43 | GitHub Issue #43}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/ */
export const TrainingSessionEncounter: IMysteryEncounter = export const TrainingSessionEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType( MysteryEncounterBuilder.withEncounterType(

View File

@ -71,8 +71,6 @@ export default interface IMysteryEncounter {
onInit?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean;
/** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */ /** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */
onVisualsStart?: (scene: BattleScene) => boolean; onVisualsStart?: (scene: BattleScene) => boolean;
/** Event right before MysteryEncounterPhase begins. Use for unshifting any phases before the actual encounter */
onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean;
/** Will provide the player party EXP before rewards are displayed for that wave */ /** Will provide the player party EXP before rewards are displayed for that wave */
doEncounterExp?: (scene: BattleScene) => boolean; doEncounterExp?: (scene: BattleScene) => boolean;
/** Will provide the player a rewards shop for that wave */ /** Will provide the player a rewards shop for that wave */
@ -392,7 +390,6 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
doEncounterRewards?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean;
onInit?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean;
onVisualsStart?: (scene: BattleScene) => boolean; onVisualsStart?: (scene: BattleScene) => boolean;
onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean;
hideBattleIntroMessage?: boolean; hideBattleIntroMessage?: boolean;
hideIntroVisuals?: boolean; hideIntroVisuals?: boolean;
@ -625,18 +622,6 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
return Object.assign(this, { onInit: onInit }); return Object.assign(this, { onInit: onInit });
} }
/**
* Event callback right before MysteryEncounterPhase begins.
* Use for unshifting any last-minute phases before the actual encounter, as all phases are cleared and reset at that point.
* Example: set the weather before encounter begins
*
* @param onVisualsStart - synchronous callback function to perform immediately before MysteryEncounterPhase begins
* @returns
*/
withOnPreMysteryEncounterPhase(onPreMysteryEncounterPhase: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onPreMysteryEncounterPhase">> {
return Object.assign(this, { onPreMysteryEncounterPhase: onPreMysteryEncounterPhase });
}
/** /**
* Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in
* *

View File

@ -50,7 +50,7 @@ export function doTrainerExclamation(scene: BattleScene) {
} }
}); });
scene.playSound("GEN8- Exclaim.wav"); scene.playSound("GEN8- Exclaim.wav", { volume: 0.8 });
} }
export interface EnemyPokemonConfig { export interface EnemyPokemonConfig {
@ -417,10 +417,10 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
* Can have shop displayed or skipped * Can have shop displayed or skipped
* @param scene - Battle Scene * @param scene - Battle Scene
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers * @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
* @param nonShopRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop) * @param nonShopPlayerItemRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop)
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase) * @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase)
*/ */
export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) { export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopPlayerItemRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) {
scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => { scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => {
if (preRewardsCallback) { if (preRewardsCallback) {
preRewardsCallback(); preRewardsCallback();
@ -432,8 +432,8 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
scene.tryRemovePhase(p => p instanceof SelectModifierPhase); scene.tryRemovePhase(p => p instanceof SelectModifierPhase);
} }
if (nonShopRewards?.length > 0) { if (nonShopPlayerItemRewards?.length > 0) {
nonShopRewards.forEach((reward) => { nonShopPlayerItemRewards.forEach((reward) => {
scene.unshiftPhase(new ModifierRewardPhase(scene, reward)); scene.unshiftPhase(new ModifierRewardPhase(scene, reward));
}); });
} else { } else {
@ -679,7 +679,7 @@ export function handleEncounterStartOfBattleEffects(scene: BattleScene) {
} else { } else {
source = scene.getEnemyField()[0]; source = scene.getEnemyField()[0];
} }
scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.followUp)); scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
}); });
// Pseudo turn end phase to reset flinch states, Endure, etc. // Pseudo turn end phase to reset flinch states, Endure, etc.

View File

@ -8,7 +8,7 @@ export const fieryFalloutDialogue = {
label: "Find the source", label: "Find the source",
tooltip: "(?) Discover the source\n(-) Hard Battle", tooltip: "(?) Discover the source\n(-) Hard Battle",
selected: `You push through the storm, and find two Volcarona in the middle of a mating dance! selected: `You push through the storm, and find two Volcarona in the middle of a mating dance!
$They don't take kindly to the interruption and attack!`, $They don't take kindly to the interruption and attack!`
}, },
2: { 2: {
label: "Hunker down", label: "Hunker down",
@ -24,5 +24,7 @@ export const fieryFalloutDialogue = {
selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance! selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance!
$Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`, $Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`,
}, },
} },
found_charcoal: `After the weather clears,\nyour {{leadPokemon}} spots something on the ground.
$@s{item_fanfare}{{leadPokemon}} gained a Charcoal!`
}; };

View File

@ -1101,11 +1101,6 @@ export class EncounterPhase extends BattlePhase {
this.scene.ui.clearText(); this.scene.ui.clearText();
this.scene.ui.getMessageHandler().hideNameText(); this.scene.ui.getMessageHandler().hideNameText();
// Can add any additional unshift phases here before MysteryEncounterPhase begins (and all phase queues are cleared)
if (this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase) {
this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase(this.scene);
}
this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene));
this.end(); this.end();
}; };

View File

@ -184,7 +184,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
pokemon.lapseTags(BattlerTagLapseType.TURN_END); pokemon.lapseTags(BattlerTagLapseType.TURN_END);
}); });
this.end(); super.end();
} }
} }

View File

@ -1,15 +1,16 @@
import { Button } from "#app/enums/buttons"; import { Button } from "#app/enums/buttons";
import { MessagePhase, VictoryPhase } from "#app/phases"; import { CommandPhase, MessagePhase, VictoryPhase } from "#app/phases";
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import GameManager from "../utils/gameManager"; import GameManager from "../utils/gameManager";
import MessageUiHandler from "#app/ui/message-ui-handler"; import MessageUiHandler from "#app/ui/message-ui-handler";
import { Status, StatusEffect } from "#app/data/status-effect";
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) {
// Handle any eventual queued messages (e.g. weather phase, etc.) // Handle any eventual queued messages (e.g. weather phase, etc.)
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>(); const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
uiHandler.processInput(Button.ACTION); uiHandler.processInput(Button.ACTION);
}); });
@ -23,7 +24,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
uiHandler.processInput(Button.ACTION); uiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.run(MysteryEncounterPhase); await game.phaseInterceptor.to(MysteryEncounterPhase, true);
// select the desired option // select the desired option
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>(); const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
@ -73,5 +74,23 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
uiHandler.processInput(Button.ACTION); uiHandler.processInput(Button.ACTION);
}); });
if (isBattle) {
await game.phaseInterceptor.to(CommandPhase);
} else {
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
} }
}
export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager) {
game.scene.clearPhaseQueue();
game.scene.clearPhaseQueueSplice();
game.scene.getEnemyParty().forEach(p => {
p.hp = 0;
p.status = new Status(StatusEffect.FAINT);
game.scene.field.remove(p);
});
game.scene.unshiftPhase(new VictoryPhase(game.scene, 0));
game.endPhase();
game.phaseInterceptor.superEndPhase();
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, true);
}

View File

@ -5,6 +5,16 @@ import { Species } from "#app/enums/species";
import GameManager from "#app/test/utils/gameManager"; import GameManager from "#app/test/utils/gameManager";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
import Battle from "#app/battle";
import { Gender } from "#app/data/gender";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import * as BattleAnims from "#app/data/battle-anims";
import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option";
import { runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounterTestUtils";
import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases";
import { Moves } from "#enums/moves";
import BattleScene from "#app/battle-scene";
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
const namespace = "mysteryEncounter:fieryFallout"; const namespace = "mysteryEncounter:fieryFallout";
/** Arcanine and Ninetails for 2 Fire types. Lapras for burnable mon. */ /** Arcanine and Ninetails for 2 Fire types. Lapras for burnable mon. */
@ -15,6 +25,7 @@ const defaultWave = 45;
describe("Fiery Fallout - Mystery Encounter", () => { describe("Fiery Fallout - Mystery Encounter", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
let game: GameManager; let game: GameManager;
let scene: BattleScene;
beforeAll(() => { beforeAll(() => {
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
@ -22,13 +33,14 @@ describe("Fiery Fallout - Mystery Encounter", () => {
beforeEach(async () => { beforeEach(async () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
scene = game.scene;
game.override.mysteryEncounterChance(100); game.override.mysteryEncounterChance(100);
game.override.startingWave(defaultWave); game.override.startingWave(defaultWave);
game.override.startingBiome(defaultBiome); game.override.startingBiome(defaultBiome);
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
new Map<Biome, MysteryEncounterType[]>([ new Map<Biome, MysteryEncounterType[]>([
[Biome.SEA, [MysteryEncounterType.FIERY_FALLOUT]], [Biome.VOLCANO, [MysteryEncounterType.FIERY_FALLOUT]],
[Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], [Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
]) ])
); );
@ -54,7 +66,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
game.override.startingBiome(Biome.MOUNTAIN); game.override.startingBiome(Biome.MOUNTAIN);
await game.runToMysteryEncounter(); await game.runToMysteryEncounter();
expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA);
}); });
it("should not run below wave 41", async () => { it("should not run below wave 41", async () => {
@ -62,7 +74,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await game.runToMysteryEncounter(); await game.runToMysteryEncounter();
expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
}); });
it("should not run above wave 179", async () => { it("should not run above wave 179", async () => {
@ -70,164 +82,186 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await game.runToMysteryEncounter(); await game.runToMysteryEncounter();
expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
}); });
// it("should set the correct dialog tokens during initialization", () => { it("should initialize fully ", async () => {
// vi.spyOn(game.scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle); vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle);
// const weatherSpy = vi.spyOn(scene.arena, "trySetWeather").mockReturnValue(true);
// const { onInit } = FieryFalloutEncounter; const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
// const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
// expect(FieryFalloutEncounter.onInit).toBeDefined();
//
// const onInitResult = onInit(game.scene);
//
// expect(FieryFalloutEncounter.dialogueTokens?.damagePercentage).toBe("25");
// expect(FieryFalloutEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]);
// expect(FieryFalloutEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]);
// expect(onInitResult).toBe(true);
// });
// describe("Option 1 - Fight 2 Volcarona", () => { const { onInit } = FieryFalloutEncounter;
// it("should have the correct properties", () => {
// const option1 = LostAtSeaEncounter.options[0];
// expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
// expect(option1.dialogue).toBeDefined();
// expect(option1.dialogue).toStrictEqual({
// buttonLabel: `${namespace}:option:1:label`,
// disabledButtonLabel: `${namespace}:option:1:label_disabled`,
// buttonTooltip: `${namespace}:option:1:tooltip`,
// disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`,
// selected: [
// {
// text: `${namespace}:option:1:selected`,
// },
// ],
// });
// });
//
// it("should award exp to surfable PKM (Blastoise)", async () => {
// const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
//
// await game.runToMysteryEncounter(defaultParty);
// const party = game.scene.getParty();
// const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
// const expBefore = blastoise.exp;
//
// await runSelectMysteryEncounterOption(game, 2);
//
// expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1));
// });
//
// it("should leave encounter without battle", async () => {
// game.override.startingWave(33);
// const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
//
// await game.runToMysteryEncounter(defaultParty);
// await runSelectMysteryEncounterOption(game, 1);
//
// expect(leaveEncounterWithoutBattleSpy).toBeCalled();
// });
//
// it("should be disabled if no surfable PKM is in party", async () => {
// // TODO
// });
// });
// describe("Option 2 - Suffer the weather", () => { expect(FieryFalloutEncounter.onInit).toBeDefined();
// it("should have the correct properties", () => {
// const option2 = LostAtSeaEncounter.options[1];
//
// expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
// expect(option2.dialogue).toBeDefined();
// expect(option2.dialogue).toStrictEqual({
// buttonLabel: `${namespace}:option:2:label`,
// disabledButtonLabel: `${namespace}:option:2:label_disabled`,
// buttonTooltip: `${namespace}:option:2:tooltip`,
// disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`,
// selected: [
// {
// text: `${namespace}:option:2:selected`,
// },
// ],
// });
// });
//
// it("should award exp to flyable PKM (Pidgeot)", async () => {
// const laprasBaseExp = 187;
// const wave = 33;
// game.override.startingWave(wave);
//
// await game.runToMysteryEncounter(defaultParty);
// const party = game.scene.getParty();
// const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
// const expBefore = pidgeot.exp;
//
// await runSelectMysteryEncounterOption(game, 2);
//
// expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1));
// });
//
// it("should leave encounter without battle", async () => {
// game.override.startingWave(33);
// const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
//
// await game.runToMysteryEncounter(defaultParty);
// await runSelectMysteryEncounterOption(game, 2);
//
// expect(leaveEncounterWithoutBattleSpy).toBeCalled();
// });
//
// it("should be disabled if no flyable PKM is in party", async () => {
// // TODO
// });
// });
// describe("Option 3 - use FIRE types", () => { const onInitResult = onInit(scene);
// it("should have the correct properties", () => {
// const option3 = LostAtSeaEncounter.options[2]; expect(FieryFalloutEncounter.enemyPartyConfigs).toEqual([
// {
// expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT); pokemonConfigs: [
// expect(option3.dialogue).toBeDefined(); {
// expect(option3.dialogue).toStrictEqual({ species: getPokemonSpecies(Species.VOLCARONA),
// buttonLabel: `${namespace}:option:3:label`, isBoss: false,
// buttonTooltip: `${namespace}:option:3:tooltip`, gender: Gender.MALE
// selected: [ },
// { {
// text: `${namespace}:option:3:selected`, species: getPokemonSpecies(Species.VOLCARONA),
// }, isBoss: false,
// ], gender: Gender.FEMALE
// }); }
// }); ],
// doubleBattle: true,
// it("should damage all (allowed in battle) party PKM by 25%", async () => { disableSwitch: true
// game.override.startingWave(33); }
// ]);
// await game.runToMysteryEncounter(defaultParty); expect(weatherSpy).toHaveBeenCalledTimes(1);
// await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled());
// const party = game.scene.getParty(); await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled());
// const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); expect(onInitResult).toBe(true);
// vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); });
//
// await runSelectMysteryEncounterOption(game, 3); describe("Option 1 - Fight 2 Volcarona", () => {
// it("should have the correct properties", () => {
// const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); const option1 = FieryFalloutEncounter.options[0];
// const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
// allowedPkm.forEach((pkm) => expect(option1.dialogue).toBeDefined();
// expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) expect(option1.dialogue).toStrictEqual({
// ); buttonLabel: `${namespace}:option:1:label`,
// buttonTooltip: `${namespace}:option:1:tooltip`,
// notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); selected: [
// }); {
// text: `${namespace}:option:1:selected`,
// it("should leave encounter without battle", async () => { },
// game.override.startingWave(33); ],
// const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); });
// });
// await game.runToMysteryEncounter(defaultParty);
// await runSelectMysteryEncounterOption(game, 3); it("should start battle against 2 Volcarona", async () => {
// const phaseSpy = vi.spyOn(scene, "pushPhase");
// expect(leaveEncounterWithoutBattleSpy).toBeCalled();
// }); await game.runToMysteryEncounter(defaultParty);
// }); await runSelectMysteryEncounterOption(game, 1, true);
const enemyField = scene.getEnemyField();
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(Species.VOLCARONA);
expect(enemyField[1].species.speciesId).toBe(Species.VOLCARONA);
expect(enemyField[0].gender).not.toEqual(enemyField[1].gender); // Should be opposite gender
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
expect(movePhases.length).toBe(4);
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.FIRE_SPIN).length).toBe(2); // Fire spin used twice before battle
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.QUIVER_DANCE).length).toBe(2); // Quiver Dance used twice before battle
});
it("should give charcoal to lead pokemon", async () => {
await game.runToMysteryEncounter(defaultParty);
await runSelectMysteryEncounterOption(game, 1, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
const leadPokemonId = scene.getParty()?.[0].id;
const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[];
const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal");
expect(charcoal).toBeDefined;
});
});
describe("Option 2 - Suffer the weather", () => {
it("should have the correct properties", () => {
const option1 = FieryFalloutEncounter.options[0];
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
expect(option1.dialogue).toBeDefined();
expect(option1.dialogue).toStrictEqual({
buttonLabel: `${namespace}:option:2:label`,
buttonTooltip: `${namespace}:option:2:tooltip`,
selected: [
{
text: `${namespace}:option:2:selected`,
},
],
});
});
it("should damage all (allowed in battle) party PKM by 25%", async () => {
game.override.startingWave(33);
await game.runToMysteryEncounter(defaultParty);
const party = scene.getParty();
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA);
vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false);
await runSelectMysteryEncounterOption(game, 3);
const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle());
const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle());
allowedPkm.forEach((pkm) =>
expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25))
);
notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp()));
});
it("should leave encounter without battle", async () => {
game.override.startingWave(33);
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(defaultParty);
await runSelectMysteryEncounterOption(game, 3);
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
});
});
describe("Option 3 - use FIRE types", () => {
it("should have the correct properties", () => {
const option1 = FieryFalloutEncounter.options[0];
expect(option1.optionMode).toBe(EncounterOptionMode.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 damage all (allowed in battle) party PKM by 25%", async () => {
game.override.startingWave(33);
await game.runToMysteryEncounter(defaultParty);
const party = scene.getParty();
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA);
vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false);
await runSelectMysteryEncounterOption(game, 3);
const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle());
const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle());
allowedPkm.forEach((pkm) =>
expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25))
);
notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp()));
});
it("should leave encounter without battle", async () => {
game.override.startingWave(33);
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(defaultParty);
await runSelectMysteryEncounterOption(game, 3);
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
});
});
}); });

View File

@ -104,7 +104,8 @@ export default class PhaseInterceptor {
[MysteryEncounterBattlePhase, this.startPhase], [MysteryEncounterBattlePhase, this.startPhase],
[MysteryEncounterRewardsPhase, this.startPhase], [MysteryEncounterRewardsPhase, this.startPhase],
[PostMysteryEncounterPhase, this.startPhase], [PostMysteryEncounterPhase, this.startPhase],
[LearnMovePhase, this.startPhase] [LearnMovePhase, this.startPhase],
// [CommonAnimPhase, this.startPhase]
]; ];
private endBySetMode = [ private endBySetMode = [