mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-27 01:06:09 +00:00
Merge pull request #117 from AsdarDevelops/delibirdy-avarice
Delibird-y Encounter
This commit is contained in:
commit
cf92c572c9
247
src/data/mystery-encounters/encounters/delibirdy-encounter.ts
Normal file
247
src/data/mystery-encounters/encounters/delibirdy-encounter.ts
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import { leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { BerryModifier, PokemonBaseStatModifier, PokemonBaseStatTotalModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, TerastallizeModifier } from "#app/modifier/modifier";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:delibirdy";
|
||||||
|
|
||||||
|
/** Berries only */
|
||||||
|
const OPTION_2_ALLOWED_MODIFIERS = [BerryModifier.name, PokemonInstantReviveModifier.name];
|
||||||
|
|
||||||
|
/** Disallowed items are berries, Reviver Seeds, and Vitamins (form change items and fusion items are not PokemonHeldItemModifiers) */
|
||||||
|
const OPTION_3_DISALLOWED_MODIFIERS = [
|
||||||
|
BerryModifier.name,
|
||||||
|
PokemonInstantReviveModifier.name,
|
||||||
|
TerastallizeModifier.name,
|
||||||
|
PokemonBaseStatModifier.name,
|
||||||
|
PokemonBaseStatTotalModifier.name
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delibird-y encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/57 | GitHub Issue #57}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const DelibirdyEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Must have enough money for it to spawn at the very least
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.DELIBIRD.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
startFrame: 38,
|
||||||
|
scale: 0.94
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: Species.DELIBIRD.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
scale: 1.06
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: Species.DELIBIRD.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
startFrame: 65,
|
||||||
|
x: 1,
|
||||||
|
y: 5,
|
||||||
|
yShadow: 5
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}:intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}:title`)
|
||||||
|
.withDescription(`${namespace}:description`)
|
||||||
|
.withQuery(`${namespace}:query`)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}:outro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 2.75)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:1:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give the player an Ability Charm
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}:option:2:select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Get Pokemon held items and filter for valid ones
|
||||||
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return OPTION_2_ALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: modifier.type.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and item selected
|
||||||
|
encounter.setDialogueToken("chosenItem", modifier.type.name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
chosenModifier: modifier,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}:invalid_selection`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const modifier = encounter.misc.chosenModifier;
|
||||||
|
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
|
||||||
|
if (modifier.type.name.includes("Berry")) {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the modifier if its stacks go to 0
|
||||||
|
modifier.stackCount -= 1;
|
||||||
|
if (modifier.stackCount === 0) {
|
||||||
|
scene.removeModifier(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}:option:3:select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Get Pokemon held items and filter for valid ones
|
||||||
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return !OPTION_3_DISALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: modifier.type.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and item selected
|
||||||
|
encounter.setDialogueToken("chosenItem", modifier.type.name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
chosenModifier: modifier,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}:invalid_selection`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const modifier = encounter.misc.chosenModifier;
|
||||||
|
// Give the player a Berry Pouch
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
||||||
|
|
||||||
|
// Remove the modifier if its stacks go to 0
|
||||||
|
modifier.stackCount -= 1;
|
||||||
|
if (modifier.stackCount === 0) {
|
||||||
|
scene.removeModifier(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
@ -80,7 +80,8 @@ export const FieryFalloutEncounter: IMysteryEncounter =
|
|||||||
repeat: true,
|
repeat: true,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
hasShadow: true,
|
hasShadow: true,
|
||||||
x: -20
|
x: -20,
|
||||||
|
startFrame: 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
spriteKey: volcaronaSpecies.getSpriteId(true ),
|
spriteKey: volcaronaSpecies.getSpriteId(true ),
|
||||||
|
@ -106,7 +106,7 @@ export const PokemonSalesmanEncounter: IMysteryEncounter =
|
|||||||
})
|
})
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL)
|
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withHasDexProgress(true)
|
.withHasDexProgress(true)
|
||||||
.withSceneMoneyRequirement(null, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
|
.withSceneMoneyRequirement(null, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import { Type } from "../type";
|
import { Type } from "../type";
|
||||||
@ -57,6 +57,10 @@ export default class MysteryEncounterOption implements MysteryEncounterOption {
|
|||||||
this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pokemonMeetsPrimaryRequirements?(scene: BattleScene, pokemon: Pokemon) {
|
||||||
|
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
||||||
|
}
|
||||||
|
|
||||||
meetsPrimaryRequirementAndPrimaryPokemonSelected?(scene: BattleScene) {
|
meetsPrimaryRequirementAndPrimaryPokemonSelected?(scene: BattleScene) {
|
||||||
if (!this.primaryPokemonRequirements) {
|
if (!this.primaryPokemonRequirements) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { ModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { ModifierType } from "#app/modifier/modifier-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
@ -744,20 +744,20 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class HeldItemRequirement extends EncounterPokemonRequirement {
|
export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||||
requiredHeldItemModifier: PokemonHeldItemModifierType[];
|
requiredHeldItemModifiers: string[];
|
||||||
minNumberOfPokemon: number;
|
minNumberOfPokemon: number;
|
||||||
invertQuery: boolean;
|
invertQuery: boolean;
|
||||||
|
|
||||||
constructor(heldItem: PokemonHeldItemModifierType | PokemonHeldItemModifierType[], minNumberOfPokemon: number = 1, invertQuery: boolean = false) {
|
constructor(heldItem: string | string[], minNumberOfPokemon: number = 1, invertQuery: boolean = false) {
|
||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemModifier = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this?.requiredHeldItemModifier?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this?.requiredHeldItemModifiers?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
|
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
|
||||||
@ -765,19 +765,26 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
|
|
||||||
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||||
if (!this.invertQuery) {
|
if (!this.invertQuery) {
|
||||||
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifier.filter((heldItem) => pokemon.getHeldItems().filter((it) => it.type.id === heldItem.id).length > 0).length > 0);
|
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifiers.some((heldItem) => {
|
||||||
|
return pokemon.getHeldItems().some((it) => {
|
||||||
|
return it.constructor.name === heldItem;
|
||||||
|
});
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed heldItems
|
// for an inverted query, we only want to get the pokemon that have any held items that are NOT in requiredHeldItemModifiers
|
||||||
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifier.filter((heldItem) => pokemon.getHeldItems().filter((it) => it.type.id === heldItem.id).length === 0).length === 0);
|
// E.g. functions as a blacklist
|
||||||
|
return partyPokemon.filter((pokemon) => pokemon.getHeldItems().filter((it) => {
|
||||||
|
return !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem);
|
||||||
|
}).length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
const requiredItems = this.requiredHeldItemModifier.filter((a) => {
|
const requiredItems = pokemon.getHeldItems().filter((it) => {
|
||||||
pokemon.getHeldItems().filter((it) => it.type.id === a.id).length > 0;
|
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem);
|
||||||
});
|
});
|
||||||
if (requiredItems.length > 0) {
|
if (requiredItems.length > 0) {
|
||||||
return ["heldItem", requiredItems[0].name];
|
return ["heldItem", requiredItems[0].type.name];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/f
|
|||||||
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";
|
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";
|
||||||
import { PokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/pokemon-salesman-encounter";
|
import { PokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/pokemon-salesman-encounter";
|
||||||
import { OfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/offer-you-cant-refuse-encounter";
|
import { OfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/offer-you-cant-refuse-encounter";
|
||||||
|
import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter";
|
||||||
|
|
||||||
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
||||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
||||||
@ -150,7 +151,8 @@ const anyBiomeEncounters: MysteryEncounterType[] = [
|
|||||||
MysteryEncounterType.FIGHT_OR_FLIGHT,
|
MysteryEncounterType.FIGHT_OR_FLIGHT,
|
||||||
MysteryEncounterType.DARK_DEAL,
|
MysteryEncounterType.DARK_DEAL,
|
||||||
MysteryEncounterType.MYSTERIOUS_CHEST,
|
MysteryEncounterType.MYSTERIOUS_CHEST,
|
||||||
MysteryEncounterType.TRAINING_SESSION
|
MysteryEncounterType.TRAINING_SESSION,
|
||||||
|
MysteryEncounterType.DELIBIRDY,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,15 +165,20 @@ const anyBiomeEncounters: MysteryEncounterType[] = [
|
|||||||
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
[Biome.TOWN, []],
|
[Biome.TOWN, []],
|
||||||
[Biome.PLAINS, [
|
[Biome.PLAINS, [
|
||||||
MysteryEncounterType.SLUMBERING_SNORLAX
|
MysteryEncounterType.SLUMBERING_SNORLAX,
|
||||||
|
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||||
]],
|
]],
|
||||||
[Biome.GRASS, [
|
[Biome.GRASS, [
|
||||||
MysteryEncounterType.SLUMBERING_SNORLAX,
|
MysteryEncounterType.SLUMBERING_SNORLAX,
|
||||||
|
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||||
|
]],
|
||||||
|
[Biome.TALL_GRASS, [
|
||||||
|
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||||
]],
|
]],
|
||||||
[Biome.TALL_GRASS, []],
|
|
||||||
[Biome.METROPOLIS, []],
|
[Biome.METROPOLIS, []],
|
||||||
[Biome.FOREST, [
|
[Biome.FOREST, [
|
||||||
MysteryEncounterType.SAFARI_ZONE
|
MysteryEncounterType.SAFARI_ZONE,
|
||||||
|
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||||
]],
|
]],
|
||||||
|
|
||||||
[Biome.SEA, [
|
[Biome.SEA, [
|
||||||
@ -230,6 +237,8 @@ export function initMysteryEncounters() {
|
|||||||
allMysteryEncounters[MysteryEncounterType.THE_STRONG_STUFF] = TheStrongStuffEncounter;
|
allMysteryEncounters[MysteryEncounterType.THE_STRONG_STUFF] = TheStrongStuffEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.POKEMON_SALESMAN] = PokemonSalesmanEncounter;
|
allMysteryEncounters[MysteryEncounterType.POKEMON_SALESMAN] = PokemonSalesmanEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.OFFER_YOU_CANT_REFUSE] = OfferYouCantRefuseEncounter;
|
allMysteryEncounters[MysteryEncounterType.OFFER_YOU_CANT_REFUSE] = OfferYouCantRefuseEncounter;
|
||||||
|
allMysteryEncounters[MysteryEncounterType.DELIBIRDY] = DelibirdyEncounter;
|
||||||
|
// allMysteryEncounters[MysteryEncounterType.ABSOLUTE_AVARICE] = Abs;
|
||||||
|
|
||||||
// Add extreme encounters to biome map
|
// Add extreme encounters to biome map
|
||||||
extremeBiomeEncounters.forEach(encounter => {
|
extremeBiomeEncounters.forEach(encounter => {
|
||||||
|
@ -13,5 +13,7 @@ export enum MysteryEncounterType {
|
|||||||
FIERY_FALLOUT,
|
FIERY_FALLOUT,
|
||||||
THE_STRONG_STUFF,
|
THE_STRONG_STUFF,
|
||||||
POKEMON_SALESMAN,
|
POKEMON_SALESMAN,
|
||||||
OFFER_YOU_CANT_REFUSE
|
OFFER_YOU_CANT_REFUSE,
|
||||||
|
DELIBIRDY,
|
||||||
|
ABSOLUTE_AVARICE
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ export class MysteryEncounterSpriteConfig {
|
|||||||
disableAnimation?: boolean = false;
|
disableAnimation?: boolean = false;
|
||||||
/** Repeat the animation. Defaults to `false` */
|
/** Repeat the animation. Defaults to `false` */
|
||||||
repeat?: boolean = false;
|
repeat?: boolean = false;
|
||||||
|
/** What frame of the animation to start on. Defaults to 0 */
|
||||||
|
startFrame?: number = 0;
|
||||||
/** Hidden at start of encounter. Defaults to `false` */
|
/** Hidden at start of encounter. Defaults to `false` */
|
||||||
hidden?: boolean = false;
|
hidden?: boolean = false;
|
||||||
/** Tint color. `0` - `1`. Higher means darker tint. */
|
/** Tint color. `0` - `1`. Higher means darker tint. */
|
||||||
@ -279,7 +281,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||||||
const trainerAnimConfig = {
|
const trainerAnimConfig = {
|
||||||
key: config.spriteKey,
|
key: config.spriteKey,
|
||||||
repeat: config?.repeat ? -1 : 0,
|
repeat: config?.repeat ? -1 : 0,
|
||||||
startFrame: 0
|
startFrame: config?.startFrame ?? 0
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tryPlaySprite(sprites[i], tintSprites[i], trainerAnimConfig);
|
this.tryPlaySprite(sprites[i], tintSprites[i], trainerAnimConfig);
|
||||||
|
@ -13,6 +13,7 @@ import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/trai
|
|||||||
import { theStrongStuffDialogue } from "#app/locales/en/mystery-encounters/the-strong-stuff-dialogue";
|
import { theStrongStuffDialogue } from "#app/locales/en/mystery-encounters/the-strong-stuff-dialogue";
|
||||||
import { pokemonSalesmanDialogue } from "#app/locales/en/mystery-encounters/pokemon-salesman-dialogue";
|
import { pokemonSalesmanDialogue } from "#app/locales/en/mystery-encounters/pokemon-salesman-dialogue";
|
||||||
import { offerYouCantRefuseDialogue } from "#app/locales/en/mystery-encounters/offer-you-cant-refuse-dialogue";
|
import { offerYouCantRefuseDialogue } from "#app/locales/en/mystery-encounters/offer-you-cant-refuse-dialogue";
|
||||||
|
import { delibirdyDialogue } from "#app/locales/en/mystery-encounters/delibirdy-dialogue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patterns that can be used:
|
* Patterns that can be used:
|
||||||
@ -50,5 +51,6 @@ export const mysteryEncounter = {
|
|||||||
fieryFallout: fieryFalloutDialogue,
|
fieryFallout: fieryFalloutDialogue,
|
||||||
theStrongStuff: theStrongStuffDialogue,
|
theStrongStuff: theStrongStuffDialogue,
|
||||||
pokemonSalesman: pokemonSalesmanDialogue,
|
pokemonSalesman: pokemonSalesmanDialogue,
|
||||||
offerYouCantRefuse: offerYouCantRefuseDialogue
|
offerYouCantRefuse: offerYouCantRefuseDialogue,
|
||||||
|
delibirdy: delibirdyDialogue,
|
||||||
} as const;
|
} as const;
|
||||||
|
31
src/locales/en/mystery-encounters/delibirdy-dialogue.ts
Normal file
31
src/locales/en/mystery-encounters/delibirdy-dialogue.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export const delibirdyDialogue = {
|
||||||
|
intro: "A pack of Delibird have appeared!",
|
||||||
|
title: "Delibird-y",
|
||||||
|
description: "The Delibirds are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?",
|
||||||
|
query: "What will you give them?",
|
||||||
|
invalid_selection: "Pokémon doesn't have that kind of item.",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Give Money",
|
||||||
|
tooltip: "(-) Give the Delibirds {{money, money}}\n(+) Receive a Gift Item",
|
||||||
|
selected: `You toss the money to the Delibirds,\nwho chatter amongst themselves excitedly.
|
||||||
|
$They turn back to you and happily give you a present!`,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Give Food",
|
||||||
|
tooltip: "(-) Give the Delibirds a Berry or Reviver Seed\n(+) Receive a Gift Item",
|
||||||
|
select_prompt: "Select an item to give.",
|
||||||
|
selected: `You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.
|
||||||
|
$They turn back to you and happily give you a present!`,
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Give an Item",
|
||||||
|
tooltip: "(-) Give the Delibirds a Held Item\n(+) Receive a Gift Item",
|
||||||
|
select_prompt: "Select an item to give.",
|
||||||
|
selected: `You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.
|
||||||
|
$They turn back to you and happily give you a present!`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
outro: `The Delibird pack happily waddles off into the distance.
|
||||||
|
$What a curious little exchange!`
|
||||||
|
};
|
@ -12,14 +12,10 @@ export const shadyVitaminDealerDialogue = {
|
|||||||
1: {
|
1: {
|
||||||
label: "The Cheap Deal",
|
label: "The Cheap Deal",
|
||||||
tooltip: "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
tooltip: "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
||||||
selected: `{{option1PrimaryName}} swims ahead, guiding you back on track.
|
|
||||||
\${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
label: "The Pricey Deal",
|
label: "The Pricey Deal",
|
||||||
tooltip: "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
tooltip: "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
||||||
selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.
|
|
||||||
\${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
label: "Leave",
|
label: "Leave",
|
||||||
|
@ -6,15 +6,21 @@ 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";
|
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||||
|
import { expect, vi } from "vitest";
|
||||||
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import PartyUiHandler from "#app/ui/party-ui-handler";
|
||||||
|
import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected
|
* Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected
|
||||||
* @param game
|
* @param game
|
||||||
* @param optionNo - human number, not index
|
* @param optionNo - human number, not index
|
||||||
|
* @param secondaryOptionSelect -
|
||||||
* @param isBattle - if selecting option should lead to battle, set to true
|
* @param isBattle - if selecting option should lead to battle, set to true
|
||||||
*/
|
*/
|
||||||
export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, isBattle: boolean = false) {
|
export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo: number } = null, isBattle: boolean = false) {
|
||||||
await runSelectMysteryEncounterOption(game, optionNo, isBattle);
|
vi.spyOn(EncounterPhaseUtils, "selectPokemonForOption");
|
||||||
|
await runSelectMysteryEncounterOption(game, optionNo, secondaryOptionSelect);
|
||||||
|
|
||||||
// run the selected options phase
|
// run the selected options phase
|
||||||
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
||||||
@ -49,7 +55,7 @@ export async function runMysteryEncounterToEnd(game: GameManager, optionNo: numb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) {
|
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo: number } = null) {
|
||||||
// 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<MessageUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
@ -73,6 +79,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that
|
uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that
|
||||||
|
|
||||||
switch (optionNo) {
|
switch (optionNo) {
|
||||||
|
default:
|
||||||
case 1:
|
case 1:
|
||||||
// no movement needed. Default cursor position
|
// no movement needed. Default cursor position
|
||||||
break;
|
break;
|
||||||
@ -89,6 +96,42 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
|
||||||
|
if (!isNaN(secondaryOptionSelect?.pokemonNo)) {
|
||||||
|
await handleSecondaryOptionSelect(game, secondaryOptionSelect.pokemonNo, secondaryOptionSelect.optionNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, optionNo: number) {
|
||||||
|
// Handle secondary option selections
|
||||||
|
const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler;
|
||||||
|
vi.spyOn(partyUiHandler, "show");
|
||||||
|
await vi.waitFor(() => expect(partyUiHandler.show).toHaveBeenCalled());
|
||||||
|
|
||||||
|
for (let i = 1; i < pokemonNo; i++) {
|
||||||
|
partyUiHandler.processInput(Button.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open options on Pokemon
|
||||||
|
partyUiHandler.processInput(Button.ACTION);
|
||||||
|
// Click "Select" on Pokemon options
|
||||||
|
partyUiHandler.processInput(Button.ACTION);
|
||||||
|
|
||||||
|
// If there is a second choice to make after selecting a Pokemon
|
||||||
|
if (!isNaN(optionNo)) {
|
||||||
|
// Wait for Summary menu to close and second options to spawn
|
||||||
|
const secondOptionUiHandler = game.scene.ui.handlers[Mode.OPTION_SELECT] as OptionSelectUiHandler;
|
||||||
|
vi.spyOn(secondOptionUiHandler, "show");
|
||||||
|
await vi.waitFor(() => expect(secondOptionUiHandler.show).toHaveBeenCalled());
|
||||||
|
|
||||||
|
// Navigate down to the correct option
|
||||||
|
for (let i = 1; i < optionNo; i++) {
|
||||||
|
secondOptionUiHandler.processInput(Button.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the option
|
||||||
|
secondOptionUiHandler.processInput(Button.ACTION);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,373 @@
|
|||||||
|
import { Biome } from "#app/enums/biome";
|
||||||
|
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption } from "#test/mystery-encounter/encounterTestUtils";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter";
|
||||||
|
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
|
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
||||||
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
|
import { generateModifierTypeOption } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
|
||||||
|
const namespace = "mysteryEncounter:delibirdy";
|
||||||
|
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
|
const defaultBiome = Biome.CAVE;
|
||||||
|
const defaultWave = 45;
|
||||||
|
|
||||||
|
describe("Delibird-y - Mystery Encounter", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
let scene: BattleScene;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
scene = game.scene;
|
||||||
|
game.override.mysteryEncounterChance(100);
|
||||||
|
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||||
|
game.override.startingWave(defaultWave);
|
||||||
|
game.override.startingBiome(defaultBiome);
|
||||||
|
|
||||||
|
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||||
|
new Map<Biome, MysteryEncounterType[]>([
|
||||||
|
[Biome.CAVE, [MysteryEncounterType.DELIBIRDY]],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
expect(DelibirdyEncounter.encounterType).toBe(MysteryEncounterType.DELIBIRDY);
|
||||||
|
expect(DelibirdyEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT);
|
||||||
|
expect(DelibirdyEncounter.dialogue).toBeDefined();
|
||||||
|
expect(DelibirdyEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||||
|
expect(DelibirdyEncounter.dialogue.outro).toStrictEqual([{ text: `${namespace}:outro` }]);
|
||||||
|
expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||||
|
expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||||
|
expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||||
|
expect(DelibirdyEncounter.options.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run below wave 10", async () => {
|
||||||
|
game.override.startingWave(9);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DELIBIRDY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run above wave 179", async () => {
|
||||||
|
game.override.startingWave(181);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not spawn if player does not have enough money", async () => {
|
||||||
|
scene.money = 0;
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DELIBIRDY);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 1 - Give them money", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option1 = DelibirdyEncounter.options[0];
|
||||||
|
expect(option1.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_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 update the player's money properly", async () => {
|
||||||
|
const initialMoney = 20000;
|
||||||
|
scene.money = initialMoney;
|
||||||
|
const updateMoneySpy = vi.spyOn(EncounterPhaseUtils, "updatePlayerMoney");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
|
||||||
|
const price = (scene.currentBattle.mysteryEncounter.options[0].requirements[0] as MoneyRequirement).requiredMoney;
|
||||||
|
|
||||||
|
expect(updateMoneySpy).toHaveBeenCalledWith(scene, -price, true, false);
|
||||||
|
expect(scene.money).toBe(initialMoney - price);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Hidden Ability Charm", async () => {
|
||||||
|
scene.money = 200000;
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
|
||||||
|
const itemModifier = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier;
|
||||||
|
|
||||||
|
expect(itemModifier).toBeDefined();
|
||||||
|
expect(itemModifier.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be disabled if player does not have enough money", async () => {
|
||||||
|
scene.money = 0;
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
|
|
||||||
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
|
await runSelectMysteryEncounterOption(game, 1);
|
||||||
|
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
scene.money = 200000;
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 2 - Give Food", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = DelibirdyEncounter.options[1];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}:option:2:select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should decrease Berry stacks and give the player a Candy Jar", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 2 Sitrus berries on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]).type;
|
||||||
|
const sitrusMod = sitrus.newModifier(scene.getParty()[0]) as BerryModifier;
|
||||||
|
sitrusMod.stackCount = 2;
|
||||||
|
await scene.addModifier(sitrusMod, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
const sitrusAfter = scene.findModifier(m => m instanceof BerryModifier);
|
||||||
|
const candyJarAfter = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier);
|
||||||
|
|
||||||
|
expect(sitrusAfter.stackCount).toBe(1);
|
||||||
|
expect(candyJarAfter).toBeDefined();
|
||||||
|
expect(candyJarAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should remove Reviver Seed and give the player a Healing Charm", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Reviver Seed on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const revSeed = generateModifierTypeOption(scene, modifierTypes.REVIVER_SEED).type;
|
||||||
|
const modifier = revSeed.newModifier(scene.getParty()[0]) as PokemonInstantReviveModifier;
|
||||||
|
modifier.stackCount = 1;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
const reviverSeedAfter = scene.findModifier(m => m instanceof PokemonInstantReviveModifier);
|
||||||
|
const healingCharmAfter = scene.findModifier(m => m instanceof HealingBoosterModifier);
|
||||||
|
|
||||||
|
expect(reviverSeedAfter).toBeUndefined();
|
||||||
|
expect(healingCharmAfter).toBeDefined();
|
||||||
|
expect(healingCharmAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be disabled if player does not have any proper items", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierTypeOption(scene, modifierTypes.SOUL_DEW).type;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]);
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
|
|
||||||
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Reviver Seed on party lead
|
||||||
|
const revSeed = generateModifierTypeOption(scene, modifierTypes.REVIVER_SEED).type;
|
||||||
|
const modifier = revSeed.newModifier(scene.getParty()[0]) as PokemonInstantReviveModifier;
|
||||||
|
modifier.stackCount = 1;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 3 - Give Item", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = DelibirdyEncounter.options[2];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}:option:3:select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should decrease held item stacks and give the player a Berry Pouch", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 2 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierTypeOption(scene, modifierTypes.SOUL_DEW).type;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]) as PokemonNatureWeightModifier;
|
||||||
|
modifier.stackCount = 2;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
const soulDewAfter = scene.findModifier(m => m instanceof PokemonNatureWeightModifier);
|
||||||
|
const berryPouchAfter = scene.findModifier(m => m instanceof PreserveBerryModifier);
|
||||||
|
|
||||||
|
expect(soulDewAfter.stackCount).toBe(1);
|
||||||
|
expect(berryPouchAfter).toBeDefined();
|
||||||
|
expect(berryPouchAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should remove held item and give the player a Berry Pouch", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierTypeOption(scene, modifierTypes.SOUL_DEW).type;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]) as PokemonNatureWeightModifier;
|
||||||
|
modifier.stackCount = 1;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
const soulDewAfter = scene.findModifier(m => m instanceof PokemonNatureWeightModifier);
|
||||||
|
const berryPouchAfter = scene.findModifier(m => m instanceof PreserveBerryModifier);
|
||||||
|
|
||||||
|
expect(soulDewAfter).toBeUndefined();
|
||||||
|
expect(berryPouchAfter).toBeDefined();
|
||||||
|
expect(berryPouchAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be disabled if player does not have any proper items", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Reviver Seed on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const revSeed = generateModifierTypeOption(scene, modifierTypes.REVIVER_SEED).type;
|
||||||
|
const modifier = revSeed.newModifier(scene.getParty()[0]);
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
|
|
||||||
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
|
await runSelectMysteryEncounterOption(game, 3);
|
||||||
|
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierTypeOption(scene, modifierTypes.SOUL_DEW).type;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]) as PokemonNatureWeightModifier;
|
||||||
|
modifier.stackCount = 1;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -152,7 +152,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||||||
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
||||||
|
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, true);
|
await runMysteryEncounterToEnd(game, 1, null, true);
|
||||||
|
|
||||||
const enemyField = scene.getEnemyField();
|
const enemyField = scene.getEnemyField();
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||||
@ -169,7 +169,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||||||
|
|
||||||
it("should give charcoal to lead pokemon", async () => {
|
it("should give charcoal to lead pokemon", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, true);
|
await runMysteryEncounterToEnd(game, 1, null, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
@ -151,12 +151,17 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
|
|
||||||
const encounterPhase = scene.getCurrentPhase();
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
const continueEncounterSpy = vi.spyOn((encounterPhase as MysteryEncounterPhase), "continueEncounter");
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
await runSelectMysteryEncounterOption(game, 1);
|
await runSelectMysteryEncounterOption(game, 1);
|
||||||
|
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
expect(continueEncounterSpy).not.toHaveBeenCalled();
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -210,12 +215,17 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
|
|
||||||
const encounterPhase = scene.getCurrentPhase();
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
const continueEncounterSpy = vi.spyOn((encounterPhase as MysteryEncounterPhase), "continueEncounter");
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
await runSelectMysteryEncounterOption(game, 1);
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
expect(continueEncounterSpy).not.toHaveBeenCalled();
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
import { ShinyRateBoosterModifier } from "#app/modifier/modifier";
|
||||||
|
|
||||||
const namespace = "mysteryEncounter:offerYouCantRefuse";
|
const namespace = "mysteryEncounter:offerYouCantRefuse";
|
||||||
/** Gyarados for Indimidate */
|
/** Gyarados for Indimidate */
|
||||||
@ -144,6 +145,16 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
|
|||||||
expect(scene.money).toBe(initialMoney + price);
|
expect(scene.money).toBe(initialMoney + price);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Shiny Charm", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.OFFER_YOU_CANT_REFUSE, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
|
||||||
|
const itemModifier = scene.findModifier(m => m instanceof ShinyRateBoosterModifier) as ShinyRateBoosterModifier;
|
||||||
|
|
||||||
|
expect(itemModifier).toBeDefined();
|
||||||
|
expect(itemModifier.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("Should remove the Pokemon from the party", async () => {
|
it("Should remove the Pokemon from the party", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.OFFER_YOU_CANT_REFUSE, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.OFFER_YOU_CANT_REFUSE, defaultParty);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ 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 * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounterTestUtils";
|
import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption } from "#test/mystery-encounter/encounterTestUtils";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
@ -13,6 +13,7 @@ import { PokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounter
|
|||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||||
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
|
|
||||||
const namespace = "mysteryEncounter:pokemonSalesman";
|
const namespace = "mysteryEncounter:pokemonSalesman";
|
||||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
@ -108,12 +109,20 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||||||
expect(onInitResult).toBe(true);
|
expect(onInitResult).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not spawn if player does not have enough money", async () => {
|
||||||
|
scene.money = 0;
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.POKEMON_SALESMAN);
|
||||||
|
});
|
||||||
|
|
||||||
describe("Option 1 - Purchase the pokemon", () => {
|
describe("Option 1 - Purchase the pokemon", () => {
|
||||||
it("should have the correct properties", () => {
|
it("should have the correct properties", () => {
|
||||||
const option1 = PokemonSalesmanEncounter.options[0];
|
const option = PokemonSalesmanEncounter.options[0];
|
||||||
expect(option1.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL);
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||||
expect(option1.dialogue).toBeDefined();
|
expect(option.dialogue).toBeDefined();
|
||||||
expect(option1.dialogue).toStrictEqual({
|
expect(option.dialogue).toStrictEqual({
|
||||||
buttonLabel: `${namespace}:option:1:label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}:option:1:tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
@ -139,6 +148,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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.POKEMON_SALESMAN, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
|
||||||
|
|
||||||
const initialPartySize = scene.getParty().length;
|
const initialPartySize = scene.getParty().length;
|
||||||
@ -150,7 +160,28 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
|||||||
expect(scene.getParty().find(p => p.name === pokemonName) instanceof PlayerPokemon).toBeTruthy();
|
expect(scene.getParty().find(p => p.name === pokemonName) instanceof PlayerPokemon).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should be disabled if player does not have enough money", async () => {
|
||||||
|
scene.money = 0;
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
|
|
||||||
|
const encounterPhase = scene.getCurrentPhase();
|
||||||
|
expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||||
|
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||||
|
vi.spyOn(scene.ui, "playError");
|
||||||
|
|
||||||
|
await runSelectMysteryEncounterOption(game, 1);
|
||||||
|
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||||
|
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||||
|
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("should leave encounter without battle", async () => {
|
it("should leave encounter without battle", async () => {
|
||||||
|
scene.money = 20000;
|
||||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
|
||||||
|
@ -196,7 +196,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
||||||
|
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, true);
|
await runMysteryEncounterToEnd(game, 2, null, true);
|
||||||
|
|
||||||
const enemyField = scene.getEnemyField();
|
const enemyField = scene.getEnemyField();
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||||
@ -220,7 +220,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
|
|
||||||
it("should have Soul Dew in rewards", async () => {
|
it("should have Soul Dew in rewards", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, true);
|
await runMysteryEncounterToEnd(game, 2, null, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
Loading…
Reference in New Issue
Block a user