mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-27 17:26:11 +00:00
add unit tests and enhancements for item overrides in tests
This commit is contained in:
parent
7b2f21dc8c
commit
132f5ac343
@ -1072,12 +1072,14 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.field.add(newTrainer);
|
this.field.add(newTrainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this once spawn rates are finalized
|
// TODO: remove these once ME spawn rates are finalized
|
||||||
// let testStartingWeight = 0;
|
// let testStartingWeight = 0;
|
||||||
// while (testStartingWeight < 3) {
|
// while (testStartingWeight < 3) {
|
||||||
// calculateMEAggregateStats(this, testStartingWeight);
|
// calculateMEAggregateStats(this, testStartingWeight);
|
||||||
// testStartingWeight += 2;
|
// testStartingWeight += 2;
|
||||||
// }
|
// }
|
||||||
|
// calculateRareSpawnAggregateStats(this, 14);
|
||||||
|
|
||||||
// Check for mystery encounter
|
// Check for mystery encounter
|
||||||
// Can only occur in place of a standard wild battle, waves 10-180
|
// Can only occur in place of a standard wild battle, waves 10-180
|
||||||
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
|
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
|
||||||
|
@ -168,11 +168,6 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter =
|
|||||||
.withTitle(`${namespace}:title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}:description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}:query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOutroDialogue([
|
|
||||||
{
|
|
||||||
text: `${namespace}:outro`,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withOnInit((scene: BattleScene) => {
|
.withOnInit((scene: BattleScene) => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
@ -423,8 +418,6 @@ function doGreedentSpriteSteal(scene: BattleScene) {
|
|||||||
|
|
||||||
function doGreedentEatBerries(scene: BattleScene) {
|
function doGreedentEatBerries(scene: BattleScene) {
|
||||||
const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(0);
|
const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(0);
|
||||||
|
|
||||||
// scene.playSound("Follow Me");
|
|
||||||
let index = 1;
|
let index = 1;
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
targets: greedentSprites,
|
targets: greedentSprites,
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
import { HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
|
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { BerryModifier, PokemonBaseStatModifier, PokemonBaseStatTotalModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, TerastallizeModifier } from "#app/modifier/modifier";
|
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonBaseStatModifier, PokemonBaseStatTotalModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PreserveBerryModifier, TerastallizeModifier } from "#app/modifier/modifier";
|
||||||
import { ModifierRewardPhase } from "#app/phases";
|
import { ModifierRewardPhase } from "#app/phases";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:delibirdy";
|
const namespace = "mysteryEncounter:delibirdy";
|
||||||
@ -39,6 +41,10 @@ export const DelibirdyEncounter: IMysteryEncounter =
|
|||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
.withSceneWaveRangeRequirement(10, 180)
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Must have enough money for it to spawn at the very least
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Must have enough money for it to spawn at the very least
|
||||||
|
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
|
||||||
|
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
||||||
|
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
||||||
|
))
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
spriteKey: Species.DELIBIRD.toString(),
|
spriteKey: Species.DELIBIRD.toString(),
|
||||||
@ -82,7 +88,7 @@ export const DelibirdyEncounter: IMysteryEncounter =
|
|||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 2.75)
|
.withSceneMoneyRequirement(0, 2.75) // Must have money to spawn
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}:option:1:label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}:option:1:tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
@ -99,7 +105,19 @@ export const DelibirdyEncounter: IMysteryEncounter =
|
|||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
// Give the player an Ability Charm
|
// Give the player an Ability Charm
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
|
// Check if the player has max stacks of that item already
|
||||||
|
const existing = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierTypeOption(scene, modifierTypes.SHELL_BELL).type as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
|
||||||
|
}
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
@ -159,11 +177,34 @@ export const DelibirdyEncounter: IMysteryEncounter =
|
|||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
const modifier = encounter.misc.chosenModifier;
|
const modifier = encounter.misc.chosenModifier;
|
||||||
|
|
||||||
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
|
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
|
||||||
if (modifier.type.name.includes("Berry")) {
|
if (modifier.type.name.includes("Berry")) {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
// Check if the player has max stacks of that Candy Jar already
|
||||||
|
const existing = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierTypeOption(scene, modifierTypes.SHELL_BELL).type as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
// Check if the player has max stacks of that Healing Charm already
|
||||||
|
const existing = scene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierTypeOption(scene, modifierTypes.SHELL_BELL).type as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the modifier if its stacks go to 0
|
// Remove the modifier if its stacks go to 0
|
||||||
@ -231,8 +272,19 @@ export const DelibirdyEncounter: IMysteryEncounter =
|
|||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
const modifier = encounter.misc.chosenModifier;
|
const modifier = encounter.misc.chosenModifier;
|
||||||
// Give the player a Berry Pouch
|
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
// Check if the player has max stacks of Berry Pouch already
|
||||||
|
const existing = scene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierTypeOption(scene, modifierTypes.SHELL_BELL).type as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the modifier if its stacks go to 0
|
// Remove the modifier if its stacks go to 0
|
||||||
modifier.stackCount -= 1;
|
modifier.stackCount -= 1;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { BattlerIndex, BattleType } from "#app/battle";
|
import { BattlerIndex, BattleType } from "#app/battle";
|
||||||
import { biomeLinks } from "#app/data/biomes";
|
import { biomeLinks, BiomePoolTier } from "#app/data/biomes";
|
||||||
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import * as Overrides from "#app/overrides";
|
import * as Overrides from "#app/overrides";
|
||||||
import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, MovePhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, MovePhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
||||||
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
@ -344,20 +344,10 @@ export function generateModifierTypeOption(scene: BattleScene, modifier: () => M
|
|||||||
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier);
|
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier);
|
||||||
let result: ModifierType = modifierTypes[modifierId]?.();
|
let result: ModifierType = modifierTypes[modifierId]?.();
|
||||||
|
|
||||||
// Gets tier of item by checking player item pool
|
// Populates item id and tier (order matters)
|
||||||
const modifierPool = getModifierPoolForType(ModifierPoolType.PLAYER);
|
result = result
|
||||||
Object.keys(modifierPool).every(modifierTier => {
|
.withIdFromFunc(modifierTypes[modifierId])
|
||||||
const modType = modifierPool[modifierTier].find(m => {
|
.withTierFromPool();
|
||||||
if (m.modifierType.id === modifierId) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (modType) {
|
|
||||||
result = modType.modifierType;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
||||||
return new ModifierTypeOption(result, 0);
|
return new ModifierTypeOption(result, 0);
|
||||||
@ -881,3 +871,72 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
|
|||||||
|
|
||||||
console.log(stats);
|
console.log(stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: remove once encounter spawn rate is finalized
|
||||||
|
* Just a helper function to calculate aggregate stats for MEs in a Classic run
|
||||||
|
* @param scene
|
||||||
|
* @param luckValue - 0 to 14
|
||||||
|
*/
|
||||||
|
export function calculateRareSpawnAggregateStats(scene: BattleScene, luckValue: number) {
|
||||||
|
const numRuns = 1000;
|
||||||
|
let run = 0;
|
||||||
|
|
||||||
|
const calculateNumRareEncounters = (): any[] => {
|
||||||
|
const bossEncountersByRarity = [0, 0, 0, 0];
|
||||||
|
scene.setSeed(Utils.randomString(24));
|
||||||
|
scene.resetSeed();
|
||||||
|
// There are 12 wild boss floors
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
// Roll boss tier
|
||||||
|
// luck influences encounter rarity
|
||||||
|
let luckModifier = 0;
|
||||||
|
if (!isNaN(luckValue)) {
|
||||||
|
luckModifier = luckValue * 0.5;
|
||||||
|
}
|
||||||
|
const tierValue = Utils.randSeedInt(64 - luckModifier);
|
||||||
|
const tier = tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
|
||||||
|
|
||||||
|
switch (tier) {
|
||||||
|
default:
|
||||||
|
case BiomePoolTier.BOSS:
|
||||||
|
++bossEncountersByRarity[0];
|
||||||
|
break;
|
||||||
|
case BiomePoolTier.BOSS_RARE:
|
||||||
|
++bossEncountersByRarity[1];
|
||||||
|
break;
|
||||||
|
case BiomePoolTier.BOSS_SUPER_RARE:
|
||||||
|
++bossEncountersByRarity[2];
|
||||||
|
break;
|
||||||
|
case BiomePoolTier.BOSS_ULTRA_RARE:
|
||||||
|
++bossEncountersByRarity[3];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bossEncountersByRarity;
|
||||||
|
};
|
||||||
|
|
||||||
|
const encounterRuns: number[][] = [];
|
||||||
|
while (run < numRuns) {
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
const bossEncountersByRarity = calculateNumRareEncounters();
|
||||||
|
encounterRuns.push(bossEncountersByRarity);
|
||||||
|
}, 1000 * run);
|
||||||
|
run++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = encounterRuns.length;
|
||||||
|
// const totalEncountersInRun = encounterRuns.map(run => run.reduce((a, b) => a + b));
|
||||||
|
// const totalMean = totalEncountersInRun.reduce((a, b) => a + b) / n;
|
||||||
|
// const totalStd = Math.sqrt(totalEncountersInRun.map(x => Math.pow(x - totalMean, 2)).reduce((a, b) => a + b) / n);
|
||||||
|
const commonMean = encounterRuns.reduce((a, b) => a + b[0], 0) / n;
|
||||||
|
const rareMean = encounterRuns.reduce((a, b) => a + b[1], 0) / n;
|
||||||
|
const superRareMean = encounterRuns.reduce((a, b) => a + b[2], 0) / n;
|
||||||
|
const ultraRareMean = encounterRuns.reduce((a, b) => a + b[3], 0) / n;
|
||||||
|
|
||||||
|
const stats = `Avg Commons: ${commonMean}\nAvg Rare: ${rareMean}\nAvg Super Rare: ${superRareMean}\nAvg Ultra Rare: ${ultraRareMean}\n`;
|
||||||
|
|
||||||
|
console.log(stats);
|
||||||
|
}
|
||||||
|
@ -109,11 +109,35 @@ export class ModifierType {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates item id for ModifierType instance
|
||||||
|
* @param func
|
||||||
|
*/
|
||||||
withIdFromFunc(func: ModifierTypeFunc): ModifierType {
|
withIdFromFunc(func: ModifierTypeFunc): ModifierType {
|
||||||
this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func);
|
this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates item tier for ModifierType instance
|
||||||
|
* Tier is a necessary field for items that appear in player shop (determines the Pokeball visual they use)
|
||||||
|
* To find the tier, this function performs a reverse lookup of the item type in modifier pools
|
||||||
|
* @param poolType - Default 'ModifierPoolType.PLAYER'. Which pool to lookup item tier from
|
||||||
|
*/
|
||||||
|
withTierFromPool(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierType {
|
||||||
|
const modifierPool = getModifierPoolForType(poolType);
|
||||||
|
Object.values(modifierPool).every(weightedModifiers => {
|
||||||
|
weightedModifiers.every(m => {
|
||||||
|
if (m.modifierType.id === this.id) {
|
||||||
|
this.tier = m.modifierType.tier;
|
||||||
|
return false; // Early lookup return if tier found
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return !this.tier; // Early lookup return if tier found
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
newModifier(...args: any[]): Modifier {
|
newModifier(...args: any[]): Modifier {
|
||||||
return this.newModifierFunc(this, args);
|
return this.newModifierFunc(this, args);
|
||||||
}
|
}
|
||||||
@ -1841,6 +1865,21 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc {
|
|||||||
return modifierTypes[id];
|
return modifierTypes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates modifier options for a SelectModifierPhase
|
||||||
|
* @param count - Determines the number of items to generate
|
||||||
|
* @param party - Party is required for generating proper modifier pools
|
||||||
|
* @param modifierTiers - (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule.
|
||||||
|
* @param customModifierSettings - (Optional) If specified, can customize the item shop rewards further.
|
||||||
|
* - `guaranteedModifierTypeOptions?: ModifierTypeOption[]` - If specified, will override the first X items to be specific modifier options (these should be pre-genned).
|
||||||
|
* - `guaranteedModifierTypeFuncs?: ModifierTypeFunc[]` - If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned).
|
||||||
|
* - `guaranteedModifierTiers?: ModifierTier[]` - If specified, will override the next X items to be the specified tier. These can upgrade with luck.
|
||||||
|
* - `fillRemaining?: boolean` - Default 'false'. If set to true, will fill the remainder of shop items that were not overridden by the 3 options above, up to the 'count' param value.
|
||||||
|
* - Example: `count = 4`, `customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true }`,
|
||||||
|
* - The first item in the shop will be `GREAT` tier, and the remaining 3 items will be generated normally.
|
||||||
|
* - If `fillRemaining = false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of `count` value).
|
||||||
|
* - `rerollMultiplier?: number` - If specified, can adjust the amount of money required for a shop reroll. If set to 0, the shop will not allow rerolls at all.
|
||||||
|
*/
|
||||||
export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings): ModifierTypeOption[] {
|
export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings): ModifierTypeOption[] {
|
||||||
const options: ModifierTypeOption[] = [];
|
const options: ModifierTypeOption[] = [];
|
||||||
const retryCount = Math.min(count * 5, 50);
|
const retryCount = Math.min(count * 5, 50);
|
||||||
@ -1849,32 +1888,21 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo
|
|||||||
options.push(getModifierTypeOptionWithRetry(options, retryCount, party, modifierTiers?.length > i ? modifierTiers[i] : undefined));
|
options.push(getModifierTypeOptionWithRetry(options, retryCount, party, modifierTiers?.length > i ? modifierTiers[i] : undefined));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Guaranteed mods first
|
// Guaranteed mod options first
|
||||||
if (customModifierSettings?.guaranteedModifierTypeOptions?.length) {
|
if (customModifierSettings?.guaranteedModifierTypeOptions?.length > 0) {
|
||||||
customModifierSettings?.guaranteedModifierTypeOptions.forEach((option) => {
|
options.push(...customModifierSettings.guaranteedModifierTypeOptions);
|
||||||
options.push(option);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guaranteed mod funcs second
|
// Guaranteed mod functions second
|
||||||
if (customModifierSettings?.guaranteedModifierTypeFuncs?.length) {
|
if (customModifierSettings?.guaranteedModifierTypeFuncs?.length > 0) {
|
||||||
customModifierSettings?.guaranteedModifierTypeFuncs.forEach((mod, i) => {
|
customModifierSettings?.guaranteedModifierTypeFuncs.forEach((mod, i) => {
|
||||||
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === mod);
|
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === mod);
|
||||||
let guaranteedMod: ModifierType = modifierTypes[modifierId]?.();
|
let guaranteedMod: ModifierType = modifierTypes[modifierId]?.();
|
||||||
|
|
||||||
// Gets tier of item by checking player item pool
|
// Populates item id and tier
|
||||||
Object.keys(modifierPool).every(modifierTier => {
|
guaranteedMod = guaranteedMod
|
||||||
const modType = modifierPool[modifierTier].find(m => {
|
.withIdFromFunc(modifierTypes[modifierId])
|
||||||
if (m.modifierType.id === modifierId) {
|
.withTierFromPool();
|
||||||
return m;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (modType) {
|
|
||||||
guaranteedMod = modType.modifierType;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const modType = guaranteedMod instanceof ModifierTypeGenerator ? guaranteedMod.generateType(party) : guaranteedMod;
|
const modType = guaranteedMod instanceof ModifierTypeGenerator ? guaranteedMod.generateType(party) : guaranteedMod;
|
||||||
const option = new ModifierTypeOption(modType, 0);
|
const option = new ModifierTypeOption(modType, 0);
|
||||||
@ -1883,7 +1911,7 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Guaranteed tiers third
|
// Guaranteed tiers third
|
||||||
if (customModifierSettings?.guaranteedModifierTiers?.length) {
|
if (customModifierSettings?.guaranteedModifierTiers?.length > 0) {
|
||||||
customModifierSettings?.guaranteedModifierTiers.forEach((tier) => {
|
customModifierSettings?.guaranteedModifierTiers.forEach((tier) => {
|
||||||
options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier));
|
options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier));
|
||||||
});
|
});
|
||||||
@ -1900,8 +1928,12 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo
|
|||||||
// OVERRIDE IF NECESSARY
|
// OVERRIDE IF NECESSARY
|
||||||
if (Overrides.ITEM_REWARD_OVERRIDE?.length) {
|
if (Overrides.ITEM_REWARD_OVERRIDE?.length) {
|
||||||
options.forEach((mod, i) => {
|
options.forEach((mod, i) => {
|
||||||
// @ts-ignore: keeps throwing don't use string as index error in typedoc run
|
let override = modifierTypes[Overrides.ITEM_REWARD_OVERRIDE[i]]?.();
|
||||||
const override = modifierTypes[Overrides.ITEM_REWARD_OVERRIDE[i]]?.();
|
// Populates item id and tier
|
||||||
|
override = override
|
||||||
|
.withIdFromFunc(modifierTypes[Overrides.ITEM_REWARD_OVERRIDE[i]])
|
||||||
|
.withTierFromPool();
|
||||||
|
|
||||||
mod.type = (override instanceof ModifierTypeGenerator ? override.generateType(party) : override) || mod.type;
|
mod.type = (override instanceof ModifierTypeGenerator ? override.generateType(party) : override) || mod.type;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import { VoucherType } from "../system/voucher";
|
|||||||
import { FormChangeItem, SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
|
import { FormChangeItem, SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
|
||||||
import { Nature } from "#app/data/nature";
|
import { Nature } from "#app/data/nature";
|
||||||
import * as Overrides from "../overrides";
|
import * as Overrides from "../overrides";
|
||||||
import { ModifierType, modifierTypes } from "./modifier-type";
|
import { ModifierType, ModifierTypeGenerator, modifierTypes } from "./modifier-type";
|
||||||
import { Command } from "#app/ui/command-ui-handler.js";
|
import { Command } from "#app/ui/command-ui-handler.js";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -2683,8 +2683,12 @@ export function overrideModifiers(scene: BattleScene, player: boolean = true): v
|
|||||||
if (!modifierTypes.hasOwnProperty(modifierName)) {
|
if (!modifierTypes.hasOwnProperty(modifierName)) {
|
||||||
return;
|
return;
|
||||||
} // if the modifier does not exist, we skip it
|
} // if the modifier does not exist, we skip it
|
||||||
const modifierType: ModifierType = modifierTypes[modifierName]();
|
let modifierType: ModifierType = modifierTypes[modifierName]();
|
||||||
const modifier: PersistentModifier = modifierType.withIdFromFunc(modifierTypes[modifierName]).newModifier() as PersistentModifier;
|
if (modifierType instanceof ModifierTypeGenerator) {
|
||||||
|
modifierType = (modifierType as ModifierTypeGenerator).generateType(scene.getParty(), [item.type]);
|
||||||
|
}
|
||||||
|
modifierType = modifierType.withIdFromFunc(modifierTypes[modifierName]);
|
||||||
|
const modifier: PersistentModifier = modifierType.newModifier(scene.getParty()[0]) as PersistentModifier;
|
||||||
modifier.stackCount = qty;
|
modifier.stackCount = qty;
|
||||||
if (player) {
|
if (player) {
|
||||||
scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
|
@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// 1 to 256, set to null to ignore
|
// 1 to 256, set to null to ignore
|
||||||
export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256;
|
export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null;
|
||||||
export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
|
export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
|
||||||
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.ABSOLUTE_AVARICE;
|
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MODIFIER / ITEM OVERRIDES
|
* MODIFIER / ITEM OVERRIDES
|
||||||
@ -137,7 +137,7 @@ export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounter
|
|||||||
* - BerryType is for BERRY
|
* - BerryType is for BERRY
|
||||||
* - SpeciesStatBoosterItem is for SPECIES_STAT_BOOSTER
|
* - SpeciesStatBoosterItem is for SPECIES_STAT_BOOSTER
|
||||||
*/
|
*/
|
||||||
interface ModifierOverride {
|
export interface ModifierOverride {
|
||||||
name: keyof typeof modifierTypes & string,
|
name: keyof typeof modifierTypes & string,
|
||||||
count?: integer
|
count?: integer
|
||||||
type?: TempBattleStat|Stat|Nature|Type|BerryType|SpeciesStatBoosterItem
|
type?: TempBattleStat|Stat|Nature|Type|BerryType|SpeciesStatBoosterItem
|
||||||
@ -155,4 +155,4 @@ export const NEVER_CRIT_OVERRIDE: boolean = false;
|
|||||||
* If less items are listed than rolled, only some items will be replaced
|
* If less items are listed than rolled, only some items will be replaced
|
||||||
* If more items are listed than rolled, only the first X items will be shown, where X is the number of items rolled.
|
* If more items are listed than rolled, only the first X items will be shown, where X is the number of items rolled.
|
||||||
*/
|
*/
|
||||||
export const ITEM_REWARD_OVERRIDE: Array<String> = [];
|
export const ITEM_REWARD_OVERRIDE: Array<keyof typeof modifierTypes & string> = [];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Button } from "#app/enums/buttons";
|
import { Button } from "#app/enums/buttons";
|
||||||
import { CommandPhase, MessagePhase, VictoryPhase } from "#app/phases";
|
import { CommandPhase, MessagePhase, VictoryPhase } from "#app/phases";
|
||||||
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
import { MysteryEncounterBattlePhase, MysteryEncounterOptionSelectedPhase, 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";
|
||||||
@ -26,29 +26,39 @@ export async function runMysteryEncounterToEnd(game: GameManager, optionNo: numb
|
|||||||
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
}, () => game.isCurrentPhase(MysteryEncounterBattlePhase) || game.isCurrentPhase(MysteryEncounterRewardsPhase));
|
||||||
|
|
||||||
// If a battle is started, fast forward to end of the battle
|
|
||||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
|
||||||
game.scene.clearPhaseQueue();
|
|
||||||
game.scene.clearPhaseQueueSplice();
|
|
||||||
game.scene.unshiftPhase(new VictoryPhase(game.scene, 0));
|
|
||||||
game.endPhase();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle end of battle trainer messages
|
|
||||||
game.onNextPrompt("TrainerVictoryPhase", Mode.MESSAGE, () => {
|
|
||||||
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle egg hatch dialogue
|
|
||||||
game.onNextPrompt("EggLapsePhase", Mode.MESSAGE, () => {
|
|
||||||
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isBattle) {
|
if (isBattle) {
|
||||||
|
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||||
|
game.setMode(Mode.MESSAGE);
|
||||||
|
game.endPhase();
|
||||||
|
}, () => game.isCurrentPhase(CommandPhase));
|
||||||
|
|
||||||
|
game.onNextPrompt("CheckSwitchPhase", Mode.MESSAGE, () => {
|
||||||
|
game.setMode(Mode.MESSAGE);
|
||||||
|
game.endPhase();
|
||||||
|
}, () => game.isCurrentPhase(CommandPhase));
|
||||||
|
|
||||||
|
// If a battle is started, fast forward to end of the battle
|
||||||
|
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||||
|
game.scene.clearPhaseQueue();
|
||||||
|
game.scene.clearPhaseQueueSplice();
|
||||||
|
game.scene.unshiftPhase(new VictoryPhase(game.scene, 0));
|
||||||
|
game.endPhase();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle end of battle trainer messages
|
||||||
|
game.onNextPrompt("TrainerVictoryPhase", Mode.MESSAGE, () => {
|
||||||
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle egg hatch dialogue
|
||||||
|
game.onNextPrompt("EggLapsePhase", Mode.MESSAGE, () => {
|
||||||
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
} else {
|
} else {
|
||||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
||||||
@ -60,7 +70,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
|
||||||
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
}, () => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase));
|
||||||
|
|
||||||
if (game.isCurrentPhase(MessagePhase)) {
|
if (game.isCurrentPhase(MessagePhase)) {
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
await game.phaseInterceptor.run(MessagePhase);
|
||||||
@ -70,7 +80,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => {
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
}, () => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase));
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, true);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, true);
|
||||||
|
|
||||||
|
@ -0,0 +1,266 @@
|
|||||||
|
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, skipBattleRunMysteryEncounterRewardsPhase } 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 * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { BerryModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { AbsoluteAvariceEncounter } from "#app/data/mystery-encounters/encounters/absolute-avarice-encounter";
|
||||||
|
import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
|
||||||
|
const namespace = "mysteryEncounter:absoluteAvarice";
|
||||||
|
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
|
const defaultBiome = Biome.PLAINS;
|
||||||
|
const defaultWave = 45;
|
||||||
|
|
||||||
|
describe("Absolute Avarice - 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.PLAINS, [MysteryEncounterType.ABSOLUTE_AVARICE]],
|
||||||
|
[Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
|
||||||
|
expect(AbsoluteAvariceEncounter.encounterType).toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
expect(AbsoluteAvariceEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT);
|
||||||
|
expect(AbsoluteAvariceEncounter.dialogue).toBeDefined();
|
||||||
|
expect(AbsoluteAvariceEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||||
|
expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||||
|
expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||||
|
expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||||
|
expect(AbsoluteAvariceEncounter.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.ABSOLUTE_AVARICE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run above wave 179", async () => {
|
||||||
|
game.override.startingWave(181);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not spawn outside of proper biomes", async () => {
|
||||||
|
game.override.startingBiome(Biome.VOLCANO);
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not spawn if player does not have enough berries", async () => {
|
||||||
|
scene.modifiers = [];
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should spawn if player has enough berries", async () => {
|
||||||
|
game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should remove all player's berries at the start of the encounter", async () => {
|
||||||
|
game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
expect(scene.modifiers?.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 1 - Fight the Greedent", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option1 = AbsoluteAvariceEncounter.options[0];
|
||||||
|
expect(option1.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option1.dialogue).toBeDefined();
|
||||||
|
expect(option1.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:1:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start battle against Greedent", async () => {
|
||||||
|
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1, null, true);
|
||||||
|
|
||||||
|
const enemyField = scene.getEnemyField();
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||||
|
expect(enemyField.length).toBe(1);
|
||||||
|
expect(enemyField[0].species.speciesId).toBe(Species.GREEDENT);
|
||||||
|
const moveset = enemyField[0].moveset.map(m => m.moveId);
|
||||||
|
expect(moveset?.length).toBe(4);
|
||||||
|
expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]);
|
||||||
|
|
||||||
|
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
|
||||||
|
expect(movePhases.length).toBe(1);
|
||||||
|
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.STUFF_CHEEKS).length).toBe(1); // Stuff Cheeks used before battle
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should give reviver seed to each pokemon after battle", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1, null, true);
|
||||||
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
|
for (const partyPokemon of scene.getParty()) {
|
||||||
|
const pokemonId = partyPokemon.id;
|
||||||
|
const pokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& (m as PokemonHeldItemModifier).pokemonId === pokemonId, true) as PokemonHeldItemModifier[];
|
||||||
|
const revSeed = pokemonItems.find(i => i.type.name === "Reviver Seed");
|
||||||
|
expect(revSeed).toBeDefined;
|
||||||
|
expect(revSeed.stackCount).toBe(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 2 - Reason with It", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = AbsoluteAvariceEncounter.options[1];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return 3 (2/5ths floored) berries if 8 were stolen", async () => {
|
||||||
|
game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 3, type: BerryType.APICOT}]);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
expect(scene.modifiers?.length).toBe(0);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
|
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier);
|
||||||
|
const berryCountAfter = berriesAfter.reduce((a, b) => a + b.stackCount, 0);
|
||||||
|
expect(berriesAfter).toBeDefined();
|
||||||
|
expect(berryCountAfter).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return 2 (2/5ths floored) berries if 7 were stolen", async () => {
|
||||||
|
game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 2, type: BerryType.APICOT}]);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||||
|
expect(scene.modifiers?.length).toBe(0);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
|
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier);
|
||||||
|
const berryCountAfter = berriesAfter.reduce((a, b) => a + b.stackCount, 0);
|
||||||
|
expect(berriesAfter).toBeDefined();
|
||||||
|
expect(berryCountAfter).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 3 - Let it have the food", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = AbsoluteAvariceEncounter.options[2];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add Greedent to the party", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
const partyCountBefore = scene.getParty().length;
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3);
|
||||||
|
const partyCountAfter = scene.getParty().length;
|
||||||
|
|
||||||
|
expect(partyCountBefore + 1).toBe(partyCountAfter);
|
||||||
|
const greedent = scene.getParty()[scene.getParty().length - 1];
|
||||||
|
expect(greedent.species.speciesId).toBe(Species.GREEDENT);
|
||||||
|
const moveset = greedent.moveset.map(m => m.moveId);
|
||||||
|
expect(moveset?.length).toBe(4);
|
||||||
|
expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 3);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -11,7 +11,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter";
|
import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter";
|
||||||
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, LevelIncrementBoosterModifier, PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
||||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import { generateModifierTypeOption } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { generateModifierTypeOption } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
@ -131,6 +131,28 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
expect(itemModifier.stackCount).toBe(1);
|
expect(itemModifier.stackCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Shell Bell if they have max stacks of Berry Pouches", async () => {
|
||||||
|
scene.money = 200000;
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// 5 Healing Charms
|
||||||
|
scene.modifiers = [];
|
||||||
|
const abilityCharm = generateModifierTypeOption(scene, modifierTypes.ABILITY_CHARM).type.newModifier() as HiddenAbilityRateBoosterModifier;
|
||||||
|
abilityCharm.stackCount = 4;
|
||||||
|
await scene.addModifier(abilityCharm, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
|
||||||
|
const abilityCharmAfter = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier);
|
||||||
|
const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier);
|
||||||
|
|
||||||
|
expect(abilityCharmAfter).toBeDefined();
|
||||||
|
expect(abilityCharmAfter.stackCount).toBe(4);
|
||||||
|
expect(shellBellAfter).toBeDefined();
|
||||||
|
expect(shellBellAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("should be disabled if player does not have enough money", async () => {
|
it("should be disabled if player does not have enough money", async () => {
|
||||||
scene.money = 0;
|
scene.money = 0;
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
@ -221,6 +243,64 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
expect(healingCharmAfter.stackCount).toBe(1);
|
expect(healingCharmAfter.stackCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Shell Bell if they have max stacks of Candy Jars", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// 99 Candy Jars
|
||||||
|
scene.modifiers = [];
|
||||||
|
const candyJar = generateModifierTypeOption(scene, modifierTypes.CANDY_JAR).type.newModifier() as LevelIncrementBoosterModifier;
|
||||||
|
candyJar.stackCount = 99;
|
||||||
|
await scene.addModifier(candyJar, true, false, false, true);
|
||||||
|
const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]).type;
|
||||||
|
|
||||||
|
// Sitrus berries on party
|
||||||
|
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);
|
||||||
|
const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier);
|
||||||
|
|
||||||
|
expect(sitrusAfter.stackCount).toBe(1);
|
||||||
|
expect(candyJarAfter).toBeDefined();
|
||||||
|
expect(candyJarAfter.stackCount).toBe(99);
|
||||||
|
expect(shellBellAfter).toBeDefined();
|
||||||
|
expect(shellBellAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Shell Bell if they have max stacks of Healing Charms", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// 5 Healing Charms
|
||||||
|
scene.modifiers = [];
|
||||||
|
const healingCharm = generateModifierTypeOption(scene, modifierTypes.HEALING_CHARM).type.newModifier() as HealingBoosterModifier;
|
||||||
|
healingCharm.stackCount = 5;
|
||||||
|
await scene.addModifier(healingCharm, true, false, false, true);
|
||||||
|
|
||||||
|
// 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});
|
||||||
|
|
||||||
|
const reviverSeedAfter = scene.findModifier(m => m instanceof PokemonInstantReviveModifier);
|
||||||
|
const healingCharmAfter = scene.findModifier(m => m instanceof HealingBoosterModifier);
|
||||||
|
const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier);
|
||||||
|
|
||||||
|
expect(reviverSeedAfter).toBeUndefined();
|
||||||
|
expect(healingCharmAfter).toBeDefined();
|
||||||
|
expect(healingCharmAfter.stackCount).toBe(5);
|
||||||
|
expect(shellBellAfter).toBeDefined();
|
||||||
|
expect(shellBellAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("should be disabled if player does not have any proper items", async () => {
|
it("should be disabled if player does not have any proper items", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
@ -325,6 +405,35 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
expect(berryPouchAfter.stackCount).toBe(1);
|
expect(berryPouchAfter.stackCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should give the player a Shell Bell if they have max stacks of Berry Pouches", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
// 5 Healing Charms
|
||||||
|
scene.modifiers = [];
|
||||||
|
const healingCharm = generateModifierTypeOption(scene, modifierTypes.BERRY_POUCH).type.newModifier() as PreserveBerryModifier;
|
||||||
|
healingCharm.stackCount = 3;
|
||||||
|
await scene.addModifier(healingCharm, true, false, false, true);
|
||||||
|
|
||||||
|
// Set 1 Soul Dew on party lead
|
||||||
|
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);
|
||||||
|
const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier);
|
||||||
|
|
||||||
|
expect(soulDewAfter).toBeUndefined();
|
||||||
|
expect(berryPouchAfter).toBeDefined();
|
||||||
|
expect(berryPouchAfter.stackCount).toBe(3);
|
||||||
|
expect(shellBellAfter).toBeDefined();
|
||||||
|
expect(shellBellAfter.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("should be disabled if player does not have any proper items", async () => {
|
it("should be disabled if player does not have any proper items", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import * as overrides from "#app/overrides";
|
|||||||
import * as GameMode from "#app/game-mode";
|
import * as GameMode from "#app/game-mode";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { GameModes, getGameMode } from "#app/game-mode";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { ModifierOverride } from "#app/overrides";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to handle overrides in tests
|
* Helper to handle overrides in tests
|
||||||
@ -117,6 +118,12 @@ export class OverridesHelper {
|
|||||||
return spy;
|
return spy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
starterHeldItems(modifiers: ModifierOverride[]) {
|
||||||
|
const spy = vi.spyOn(Overrides, "STARTING_MODIFIER_OVERRIDE", "get").mockReturnValue(modifiers);
|
||||||
|
this.log(`Starting modifiers set to ${modifiers.map(m => JSON.stringify(m)).join(", ")}!`);
|
||||||
|
return spy;
|
||||||
|
}
|
||||||
|
|
||||||
private log(...params: any[]) {
|
private log(...params: any[]) {
|
||||||
console.log("Overrides:", ...params);
|
console.log("Overrides:", ...params);
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,14 @@ import {
|
|||||||
PostMysteryEncounterPhase
|
PostMysteryEncounterPhase
|
||||||
} from "#app/phases/mystery-encounter-phases";
|
} from "#app/phases/mystery-encounter-phases";
|
||||||
|
|
||||||
|
export interface PromptHandler {
|
||||||
|
phaseTarget?;
|
||||||
|
mode?;
|
||||||
|
callback?;
|
||||||
|
expireFn?;
|
||||||
|
awaitingActionInput?;
|
||||||
|
}
|
||||||
|
|
||||||
export default class PhaseInterceptor {
|
export default class PhaseInterceptor {
|
||||||
public scene;
|
public scene;
|
||||||
public phases = {};
|
public phases = {};
|
||||||
@ -56,7 +64,7 @@ export default class PhaseInterceptor {
|
|||||||
private interval;
|
private interval;
|
||||||
private promptInterval;
|
private promptInterval;
|
||||||
private intervalRun;
|
private intervalRun;
|
||||||
private prompts;
|
private prompts: PromptHandler[];
|
||||||
private phaseFrom;
|
private phaseFrom;
|
||||||
private inProgress;
|
private inProgress;
|
||||||
private originalSetMode;
|
private originalSetMode;
|
||||||
@ -337,6 +345,7 @@ export default class PhaseInterceptor {
|
|||||||
* @param mode - The mode of the UI.
|
* @param mode - The mode of the UI.
|
||||||
* @param callback - The callback function to execute.
|
* @param callback - The callback function to execute.
|
||||||
* @param expireFn - The function to determine if the prompt has expired.
|
* @param expireFn - The function to determine if the prompt has expired.
|
||||||
|
* @param awaitingActionInput
|
||||||
*/
|
*/
|
||||||
addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn: () => void, awaitingActionInput: boolean = false) {
|
addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn: () => void, awaitingActionInput: boolean = false) {
|
||||||
this.prompts.push({
|
this.prompts.push({
|
||||||
|
Loading…
Reference in New Issue
Block a user