mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-26 16:56:11 +00:00
balance changes and bug fixes to MEs
This commit is contained in:
parent
0db39f9a1d
commit
709e1b3148
@ -2920,8 +2920,8 @@ export default class BattleScene extends SceneBase {
|
||||
this.shiftPhase();
|
||||
}
|
||||
|
||||
applyPartyExp(expValue: number): void {
|
||||
const participantIds = this.currentBattle.playerParticipantIds;
|
||||
applyPartyExp(expValue: number, pokemonDefeated: boolean, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>): void {
|
||||
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
|
||||
const party = this.getParty();
|
||||
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
||||
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
||||
@ -2929,8 +2929,12 @@ export default class BattleScene extends SceneBase {
|
||||
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.getMaxExpLevel());
|
||||
const partyMemberExp: number[] = [];
|
||||
// EXP value calculation is based off Pokemon.getExpValue
|
||||
if (useWaveIndexMultiplier) {
|
||||
expValue = Math.floor(expValue * this.currentBattle.waveIndex / 5 + 1);
|
||||
}
|
||||
|
||||
if (participantIds.size) {
|
||||
if (participantIds.size > 0) {
|
||||
if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||
expValue = Math.floor(expValue * 1.5);
|
||||
} else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) {
|
||||
@ -2939,7 +2943,7 @@ export default class BattleScene extends SceneBase {
|
||||
for (const partyMember of nonFaintedPartyMembers) {
|
||||
const pId = partyMember.id;
|
||||
const participated = participantIds.has(pId);
|
||||
if (participated) {
|
||||
if (participated && pokemonDefeated) {
|
||||
partyMember.addFriendship(2);
|
||||
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
|
||||
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
@ -132,13 +132,16 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
|
||||
const oricorioData = new PokemonData(enemyPokemon);
|
||||
const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData);
|
||||
oricorio.setVisible(false);
|
||||
oricorio.loadAssets().then(() => oricorio.setVisible(true));
|
||||
|
||||
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||
scene.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
|
||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||
scene.field.remove(enemyPokemon, true);
|
||||
});
|
||||
scene.currentBattle.enemyParty = [oricorio];
|
||||
scene.field.add(oricorio);
|
||||
// Spawns on offscreen field
|
||||
oricorio.x -= 300;
|
||||
encounter.loadAssets.push(oricorio.loadAssets());
|
||||
|
||||
const config: EnemyPartyConfig = {
|
||||
levelAdditiveMultiplier: 1,
|
||||
@ -177,8 +180,6 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
@ -186,6 +187,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
ignorePp: true
|
||||
});
|
||||
|
||||
await hideOricorioPokemon(scene);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
})
|
||||
@ -220,6 +222,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
// Learn its Dance
|
||||
hideOricorioPokemon(scene);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
})
|
||||
.build()
|
||||
@ -291,10 +294,28 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
}
|
||||
}
|
||||
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
hideOricorioPokemon(scene);
|
||||
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
function hideOricorioPokemon(scene: BattleScene) {
|
||||
return new Promise<void>(resolve => {
|
||||
const oricorioSprite = scene.getEnemyParty()[0];
|
||||
scene.tweens.add({
|
||||
targets: oricorioSprite,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
alpha: 0,
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 750,
|
||||
onComplete: () => {
|
||||
scene.field.remove(oricorioSprite, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
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
|
||||
.withSceneRequirement(new MoneyRequirement(0, 2)) // 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)
|
||||
@ -91,7 +91,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder
|
||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||
.withSceneMoneyRequirement(0, 2.75) // Must have money to spawn
|
||||
.withSceneMoneyRequirement(0, 2) // Must have money to spawn
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||
|
@ -10,6 +10,7 @@ import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Stat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
/** i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounter:fieldTrip";
|
||||
@ -60,11 +61,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
@ -75,44 +71,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
label: move.getName(),
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
const correctMove = move.getMove().category === MoveCategory.PHYSICAL;
|
||||
encounter.setDialogueToken("moveCategory", "Physical");
|
||||
if (!correctMove) {
|
||||
encounter.options[0].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.incorrect`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.lesson_learned`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_bad`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[0].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_good`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, [pokemon.id], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
};
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.physical`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -146,11 +106,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}.option.2.label`,
|
||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
@ -161,50 +116,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
label: move.getName(),
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
const correctMove = move.getMove().category === MoveCategory.SPECIAL;
|
||||
encounter.setDialogueToken("moveCategory", "Special");
|
||||
if (!correctMove) {
|
||||
encounter.options[1].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.incorrect`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.lesson_learned`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_bad`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_bad`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[1].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_good`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, [pokemon.id], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
};
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.special`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -238,11 +151,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}.option.3.label`,
|
||||
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
@ -253,44 +161,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
label: move.getName(),
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
const correctMove = move.getMove().category === MoveCategory.STATUS;
|
||||
encounter.setDialogueToken("moveCategory", "Status");
|
||||
if (!correctMove) {
|
||||
encounter.options[2].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.incorrect`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.lesson_learned`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_bad`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[2].dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
];
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}.outro_good`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, [pokemon.id], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
};
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.status`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -318,3 +190,42 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const correctMove = move.getMove().category === correctMoveCategory;
|
||||
if (!correctMove) {
|
||||
encounter.selectedOption!.dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.incorrect`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.incorrect_exp`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.selectedOption!.dialogue!.selected = [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.correct`,
|
||||
speaker: `${namespace}.speaker`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.correct_exp`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, [pokemon.id], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
};
|
||||
}
|
||||
|
@ -83,6 +83,9 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
|
||||
},
|
||||
|
@ -224,6 +224,10 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
* Defaults to 1
|
||||
*/
|
||||
expMultiplier: number;
|
||||
/**
|
||||
* Can add any asset load promises here during onInit() to make sure the scene awaits the loads properly
|
||||
*/
|
||||
loadAssets: Promise<void>[];
|
||||
/**
|
||||
* Generic property to set any custom data required for the encounter
|
||||
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
||||
@ -260,6 +264,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
this.introVisuals = undefined;
|
||||
this.misc = null;
|
||||
this.expMultiplier = 1;
|
||||
this.loadAssets = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,6 @@ import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encount
|
||||
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
||||
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
||||
import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
@ -27,7 +26,6 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||
import { TrainerConfig, trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
|
||||
import PokemonSpecies from "#app/data/pokemon-species";
|
||||
import Overrides from "#app/overrides";
|
||||
import { Egg, IEggOptions } from "#app/data/egg";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
@ -36,9 +34,8 @@ import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||
import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { ExpPhase } from "#app/phases/exp-phase";
|
||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||
|
||||
/**
|
||||
* Animates exclamation sprite over trainer's head at start of encounter
|
||||
@ -146,7 +143,9 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
battle.enemyLevels = new Array(numEnemies).fill(null).map(() => scene.currentBattle.getLevelForWave());
|
||||
}
|
||||
|
||||
scene.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
|
||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||
scene.field.remove(enemyPokemon, true);
|
||||
});
|
||||
battle.enemyParty = [];
|
||||
battle.double = doubleBattle;
|
||||
|
||||
@ -635,90 +634,11 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
|
||||
* https://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_effort_value_yield_(Generation_IX)
|
||||
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
||||
*/
|
||||
export function setEncounterExp(scene: BattleScene, participantId: integer | integer[], baseExpValue: number, useWaveIndex: boolean = true) {
|
||||
export function setEncounterExp(scene: BattleScene, participantId: number | number[], baseExpValue: number, useWaveIndex: boolean = true) {
|
||||
const participantIds = Array.isArray(participantId) ? participantId : [participantId];
|
||||
|
||||
scene.currentBattle.mysteryEncounter!.doEncounterExp = (scene: BattleScene) => {
|
||||
const party = scene.getParty();
|
||||
const expShareModifier = scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
||||
const expBalanceModifier = scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
||||
const multipleParticipantExpBonusModifier = scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
|
||||
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel());
|
||||
const partyMemberExp: number[] = [];
|
||||
// EXP value calculation is based off Pokemon.getExpValue
|
||||
let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1);
|
||||
|
||||
if (participantIds?.length > 0) {
|
||||
if (scene.currentBattle.mysteryEncounter!.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||
expValue = Math.floor(expValue * 1.5);
|
||||
}
|
||||
for (const partyMember of nonFaintedPartyMembers) {
|
||||
const pId = partyMember.id;
|
||||
const participated = participantIds.includes(pId);
|
||||
if (participated) {
|
||||
partyMember.addFriendship(2);
|
||||
}
|
||||
if (!expPartyMembers.includes(partyMember)) {
|
||||
continue;
|
||||
}
|
||||
if (!participated && !expShareModifier) {
|
||||
partyMemberExp.push(0);
|
||||
continue;
|
||||
}
|
||||
let expMultiplier = 0;
|
||||
if (participated) {
|
||||
expMultiplier += (1 / participantIds.length);
|
||||
if (participantIds.length > 1 && multipleParticipantExpBonusModifier) {
|
||||
expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2;
|
||||
}
|
||||
} else if (expShareModifier) {
|
||||
expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.length;
|
||||
}
|
||||
if (partyMember.pokerus) {
|
||||
expMultiplier *= 1.5;
|
||||
}
|
||||
if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) {
|
||||
expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE;
|
||||
}
|
||||
const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier);
|
||||
scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp);
|
||||
partyMemberExp.push(Math.floor(pokemonExp.value));
|
||||
}
|
||||
|
||||
if (expBalanceModifier) {
|
||||
let totalLevel = 0;
|
||||
let totalExp = 0;
|
||||
expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
totalExp += partyMemberExp[epm];
|
||||
totalLevel += expPartyMember.level;
|
||||
});
|
||||
|
||||
const medianLevel = Math.floor(totalLevel / expPartyMembers.length);
|
||||
|
||||
const recipientExpPartyMemberIndexes: number[] = [];
|
||||
expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
if (expPartyMember.level <= medianLevel) {
|
||||
recipientExpPartyMemberIndexes.push(epm);
|
||||
}
|
||||
});
|
||||
|
||||
const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length);
|
||||
|
||||
expPartyMembers.forEach((_partyMember, pm) => {
|
||||
partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount());
|
||||
});
|
||||
}
|
||||
|
||||
for (let pm = 0; pm < expPartyMembers.length; pm++) {
|
||||
const exp = partyMemberExp[pm];
|
||||
|
||||
if (exp) {
|
||||
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
||||
scene.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(scene, partyMemberIndex, exp) : new ShowPartyExpBarPhase(scene, partyMemberIndex, exp));
|
||||
}
|
||||
}
|
||||
}
|
||||
scene.unshiftPhase(new PartyExpPhase(scene, baseExpValue, useWaveIndex, new Set(participantIds)));
|
||||
|
||||
return true;
|
||||
};
|
||||
|
@ -7,7 +7,6 @@ import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier,
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
|
||||
import { getStatusEffectCatchRateMultiplier, StatusEffect } from "#app/data/status-effect";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { achvs } from "#app/system/achv";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||
@ -482,7 +481,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
|
||||
* @param isObtain
|
||||
*/
|
||||
export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise<void> {
|
||||
scene.unshiftPhase(new VictoryPhase(scene, BattlerIndex.ENEMY, true));
|
||||
scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true));
|
||||
|
||||
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||
|
||||
|
@ -18,11 +18,14 @@
|
||||
"label": "A Status Move",
|
||||
"tooltip": "(+) Status Item Rewards"
|
||||
},
|
||||
"selected": "{{pokeName}} shows off an awesome display of {{move}}!",
|
||||
"incorrect": "...$That isn't a {{moveCategory}} move!$I'm sorry, but I can't give you anything.",
|
||||
"lesson_learned": "Looks like you learned a valuable lesson?$Your Pokémon also gained some knowledge."
|
||||
"selected": "{{pokeName}} shows off an awesome display of {{move}}!"
|
||||
},
|
||||
"second_option_prompt": "Choose a move for your Pokémon to use.",
|
||||
"outro_good": "Thank you so much for your kindness!\nI hope the items I had were helpful!",
|
||||
"outro_bad": "Come along children, we'll\nfind a better demonstration elsewhere."
|
||||
"incorrect": "...$That isn't a {{moveCategory}} move!\nI'm sorry, but I can't give you anything.$Come along children, we'll\nfind a better demonstration elsewhere.",
|
||||
"incorrect_exp": "Looks like you learned a valuable lesson?$Your Pokémon also gained some experience.",
|
||||
"correct": "Thank you so much for your kindness!\nI hope these items might be of use to you!",
|
||||
"correct_exp": "{{pokeName}} also gained some valuable experience!",
|
||||
"status": "Status",
|
||||
"physical": "Physical",
|
||||
"special": "Special"
|
||||
}
|
@ -87,7 +87,11 @@ export class EncounterPhase extends BattlePhase {
|
||||
|
||||
let totalBst = 0;
|
||||
|
||||
battle.enemyLevels?.forEach((level, e) => {
|
||||
battle.enemyLevels?.every((level, e) => {
|
||||
if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||
// Skip enemy loading for MEs, those are loaded elsewhere
|
||||
return false;
|
||||
}
|
||||
if (!this.loaded) {
|
||||
if (battle.battleType === BattleType.TRAINER) {
|
||||
battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here?
|
||||
@ -138,6 +142,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
||||
|
||||
console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (this.scene.getParty().filter(p => p.isShiny()).length === 6) {
|
||||
@ -151,7 +156,12 @@ export class EncounterPhase extends BattlePhase {
|
||||
const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter);
|
||||
battle.mysteryEncounter = newEncounter;
|
||||
}
|
||||
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals!.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));
|
||||
if (battle.mysteryEncounter.introVisuals) {
|
||||
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));
|
||||
}
|
||||
if (battle.mysteryEncounter.loadAssets.length > 0) {
|
||||
loadEnemyAssets.push(...battle.mysteryEncounter.loadAssets);
|
||||
}
|
||||
// Load Mystery Encounter Exclamation bubble and sfx
|
||||
loadEnemyAssets.push(new Promise<void>(resolve => {
|
||||
this.scene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav");
|
||||
@ -176,7 +186,10 @@ export class EncounterPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
Promise.all(loadEnemyAssets).then(() => {
|
||||
battle.enemyParty.forEach((enemyPokemon, e) => {
|
||||
battle.enemyParty.every((enemyPokemon, e) => {
|
||||
if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||
return false;
|
||||
}
|
||||
if (e < (battle.double ? 2 : 1)) {
|
||||
if (battle.battleType === BattleType.WILD) {
|
||||
this.scene.field.add(enemyPokemon);
|
||||
@ -189,16 +202,15 @@ export class EncounterPhase extends BattlePhase {
|
||||
} else if (battle.battleType === BattleType.TRAINER) {
|
||||
enemyPokemon.setVisible(false);
|
||||
this.scene.currentBattle.trainer?.tint(0, 0.5);
|
||||
} else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||
// TODO: this may not be necessary, but leaving as placeholder
|
||||
}
|
||||
if (battle.double) {
|
||||
enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!this.loaded) {
|
||||
if (!this.loaded && battle.battleType !== BattleType.MYSTERY_ENCOUNTER) {
|
||||
regenerateModifierPoolThresholds(this.scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD);
|
||||
this.scene.generateEnemyModifiers();
|
||||
}
|
||||
|
@ -3,17 +3,21 @@ import { Phase } from "#app/phase";
|
||||
|
||||
export class PartyExpPhase extends Phase {
|
||||
expValue: number;
|
||||
useWaveIndexMultiplier?: boolean;
|
||||
pokemonParticipantIds?: Set<number>;
|
||||
|
||||
constructor(scene: BattleScene, expValue: number) {
|
||||
constructor(scene: BattleScene, expValue: number, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>) {
|
||||
super(scene);
|
||||
|
||||
this.expValue = expValue;
|
||||
this.useWaveIndexMultiplier = useWaveIndexMultiplier;
|
||||
this.pokemonParticipantIds = pokemonParticipantIds;
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.scene.applyPartyExp(this.expValue);
|
||||
this.scene.applyPartyExp(this.expValue, false, this.useWaveIndexMultiplier, this.pokemonParticipantIds);
|
||||
|
||||
this.end();
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||
/** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */
|
||||
isExpOnly: boolean;
|
||||
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, isExpOnly: boolean = false) {
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex | integer, isExpOnly: boolean = false) {
|
||||
super(scene, battlerIndex);
|
||||
|
||||
this.isExpOnly = isExpOnly;
|
||||
@ -28,7 +28,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||
this.scene.gameData.gameStats.pokemonDefeated++;
|
||||
|
||||
const expValue = this.getPokemon().getExpValue();
|
||||
this.scene.applyPartyExp(expValue);
|
||||
this.scene.applyPartyExp(expValue, true);
|
||||
|
||||
if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||
handleMysteryEncounterVictory(this.scene, false, this.isExpOnly);
|
||||
|
@ -0,0 +1,234 @@
|
||||
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 } from "#test/mystery-encounter/encounter-test-utils";
|
||||
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 { FieldTripEncounter } from "#app/data/mystery-encounters/encounters/field-trip-encounter";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
|
||||
const namespace = "mysteryEncounter:fieldTrip";
|
||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.CAVE;
|
||||
const defaultWave = 45;
|
||||
|
||||
describe("Field Trip - 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.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWaves();
|
||||
game.override.moveset([Moves.TACKLE, Moves.UPROAR, Moves.SWORDS_DANCE]);
|
||||
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||
new Map<Biome, MysteryEncounterType[]>([
|
||||
[Biome.CAVE, [MysteryEncounterType.FIELD_TRIP]],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
|
||||
expect(FieldTripEncounter.encounterType).toBe(MysteryEncounterType.FIELD_TRIP);
|
||||
expect(FieldTripEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||
expect(FieldTripEncounter.dialogue).toBeDefined();
|
||||
expect(FieldTripEncounter.dialogue.intro).toStrictEqual([
|
||||
{
|
||||
text: `${namespace}.intro`
|
||||
},
|
||||
{
|
||||
speaker: `${namespace}.speaker`,
|
||||
text: `${namespace}.intro_dialogue`
|
||||
}
|
||||
]);
|
||||
expect(FieldTripEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
|
||||
expect(FieldTripEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`);
|
||||
expect(FieldTripEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`);
|
||||
expect(FieldTripEncounter.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.FIELD_TRIP);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("Option 1 - Show off a physical move", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = FieldTripEncounter.options[0];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
});
|
||||
});
|
||||
|
||||
it("Should give no reward on incorrect option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 2 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("Should give proper rewards on correct Physical move option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(4);
|
||||
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Attack");
|
||||
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Defense");
|
||||
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("X Speed");
|
||||
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("Dire Hit");
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 });
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - Give Food", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = FieldTripEncounter.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`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
});
|
||||
});
|
||||
|
||||
it("Should give no reward on incorrect option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("Should give proper rewards on correct Special move option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 2 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(4);
|
||||
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Sp. Atk");
|
||||
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Sp. Def");
|
||||
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("X Speed");
|
||||
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("Dire Hit");
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 2 });
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 3 - Give Item", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = FieldTripEncounter.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`,
|
||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||
});
|
||||
});
|
||||
|
||||
it("Should give no reward on incorrect option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("Should give proper rewards on correct Special move option", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 3 });
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(4);
|
||||
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Accuracy");
|
||||
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Speed");
|
||||
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("5x Great Ball");
|
||||
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("IV Scanner");
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 3 });
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -208,8 +208,9 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
||||
expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE);
|
||||
expect(enemyField[0].summonData.statStages).toEqual([0, 2, 0, 2, 0, 0, 0]);
|
||||
const shuckleItems = enemyField[0].getHeldItems();
|
||||
expect(shuckleItems.length).toBe(4);
|
||||
expect(shuckleItems.length).toBe(5);
|
||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.SITRUS)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.ENIGMA)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.GANLON)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.APICOT)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.LUM)?.stackCount).toBe(2);
|
||||
|
Loading…
Reference in New Issue
Block a user