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();
|
this.shiftPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPartyExp(expValue: number): void {
|
applyPartyExp(expValue: number, pokemonDefeated: boolean, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>): void {
|
||||||
const participantIds = this.currentBattle.playerParticipantIds;
|
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
|
||||||
const party = this.getParty();
|
const party = this.getParty();
|
||||||
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
||||||
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
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 nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.getMaxExpLevel());
|
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.getMaxExpLevel());
|
||||||
const partyMemberExp: number[] = [];
|
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) {
|
if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||||
expValue = Math.floor(expValue * 1.5);
|
expValue = Math.floor(expValue * 1.5);
|
||||||
} else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) {
|
} 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) {
|
for (const partyMember of nonFaintedPartyMembers) {
|
||||||
const pId = partyMember.id;
|
const pId = partyMember.id;
|
||||||
const participated = participantIds.has(pId);
|
const participated = participantIds.has(pId);
|
||||||
if (participated) {
|
if (participated && pokemonDefeated) {
|
||||||
partyMember.addFriendship(2);
|
partyMember.addFriendship(2);
|
||||||
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
|
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
|
||||||
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) {
|
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 Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -132,13 +132,16 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
|
|
||||||
const oricorioData = new PokemonData(enemyPokemon);
|
const oricorioData = new PokemonData(enemyPokemon);
|
||||||
const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData);
|
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)
|
// 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.currentBattle.enemyParty = [oricorio];
|
||||||
scene.field.add(oricorio);
|
scene.field.add(oricorio);
|
||||||
|
// Spawns on offscreen field
|
||||||
|
oricorio.x -= 300;
|
||||||
|
encounter.loadAssets.push(oricorio.loadAssets());
|
||||||
|
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 1,
|
levelAdditiveMultiplier: 1,
|
||||||
|
@ -177,8 +180,6 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
// Pick battle
|
// Pick battle
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
|
||||||
|
|
||||||
encounter.startOfBattleEffects.push({
|
encounter.startOfBattleEffects.push({
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
@ -186,6 +187,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
ignorePp: true
|
ignorePp: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await hideOricorioPokemon(scene);
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
|
||||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
})
|
})
|
||||||
|
@ -220,6 +222,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
// Learn its Dance
|
// Learn its Dance
|
||||||
|
hideOricorioPokemon(scene);
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
|
@ -291,10 +294,28 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
hideOricorioPokemon(scene);
|
||||||
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
|
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.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)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
||||||
.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)) // 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
|
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
|
||||||
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
||||||
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
||||||
|
@ -91,7 +91,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
.withOption(
|
.withOption(
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 2.75) // Must have money to spawn
|
.withSceneMoneyRequirement(0, 2) // 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`,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"
|
||||||
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 { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
/** i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:fieldTrip";
|
const namespace = "mysteryEncounter:fieldTrip";
|
||||||
|
@ -60,11 +61,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
buttonLabel: `${namespace}.option.1.label`,
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.selected`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
@ -75,44 +71,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
label: move.getName(),
|
label: move.getName(),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
// Pokemon and move selected
|
// Pokemon and move selected
|
||||||
const correctMove = move.getMove().category === MoveCategory.PHYSICAL;
|
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.physical`));
|
||||||
encounter.setDialogueToken("moveCategory", "Physical");
|
pokemonAndMoveChosen(scene, pokemon, move, 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,
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -146,11 +106,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
buttonLabel: `${namespace}.option.2.label`,
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.selected`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
@ -161,50 +116,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
label: move.getName(),
|
label: move.getName(),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
// Pokemon and move selected
|
// Pokemon and move selected
|
||||||
const correctMove = move.getMove().category === MoveCategory.SPECIAL;
|
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.special`));
|
||||||
encounter.setDialogueToken("moveCategory", "Special");
|
pokemonAndMoveChosen(scene, pokemon, move, 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,
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -238,11 +151,6 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
buttonLabel: `${namespace}.option.3.label`,
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
buttonTooltip: `${namespace}.option.3.tooltip`,
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.selected`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
@ -253,44 +161,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
label: move.getName(),
|
label: move.getName(),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
// Pokemon and move selected
|
// Pokemon and move selected
|
||||||
const correctMove = move.getMove().category === MoveCategory.STATUS;
|
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.status`));
|
||||||
encounter.setDialogueToken("moveCategory", "Status");
|
pokemonAndMoveChosen(scene, pokemon, move, 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,
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -318,3 +190,42 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.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.SITRUS]) as PokemonHeldItemModifierType
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType
|
||||||
|
},
|
||||||
{
|
{
|
||||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
|
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
|
||||||
},
|
},
|
||||||
|
|
|
@ -224,6 +224,10 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
* Defaults to 1
|
* Defaults to 1
|
||||||
*/
|
*/
|
||||||
expMultiplier: number;
|
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
|
* Generic property to set any custom data required for the encounter
|
||||||
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
||||||
|
@ -260,6 +264,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
this.introVisuals = undefined;
|
this.introVisuals = undefined;
|
||||||
this.misc = null;
|
this.misc = null;
|
||||||
this.expMultiplier = 1;
|
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 { 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 { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
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 { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
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 { Status, StatusEffect } from "#app/data/status-effect";
|
||||||
import { TrainerConfig, trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerConfig, trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
|
||||||
import PokemonSpecies from "#app/data/pokemon-species";
|
import PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import Overrides from "#app/overrides";
|
|
||||||
import { Egg, IEggOptions } from "#app/data/egg";
|
import { Egg, IEggOptions } from "#app/data/egg";
|
||||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
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 { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { GameOverPhase } from "#app/phases/game-over-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 { 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
|
* 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());
|
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.enemyParty = [];
|
||||||
battle.double = doubleBattle;
|
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)
|
* 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
|
* @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];
|
const participantIds = Array.isArray(participantId) ? participantId : [participantId];
|
||||||
|
|
||||||
scene.currentBattle.mysteryEncounter!.doEncounterExp = (scene: BattleScene) => {
|
scene.currentBattle.mysteryEncounter!.doEncounterExp = (scene: BattleScene) => {
|
||||||
const party = scene.getParty();
|
scene.unshiftPhase(new PartyExpPhase(scene, baseExpValue, useWaveIndex, new Set(participantIds)));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier,
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
|
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
|
||||||
import { getStatusEffectCatchRateMultiplier, StatusEffect } from "#app/data/status-effect";
|
import { getStatusEffectCatchRateMultiplier, StatusEffect } from "#app/data/status-effect";
|
||||||
import { BattlerIndex } from "#app/battle";
|
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
|
@ -482,7 +481,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
|
||||||
* @param isObtain
|
* @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> {
|
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();
|
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
"label": "A Status Move",
|
"label": "A Status Move",
|
||||||
"tooltip": "(+) Status Item Rewards"
|
"tooltip": "(+) Status Item Rewards"
|
||||||
},
|
},
|
||||||
"selected": "{{pokeName}} shows off an awesome display of {{move}}!",
|
"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."
|
|
||||||
},
|
},
|
||||||
"second_option_prompt": "Choose a move for your Pokémon to use.",
|
"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!",
|
"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.",
|
||||||
"outro_bad": "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;
|
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 (!this.loaded) {
|
||||||
if (battle.battleType === BattleType.TRAINER) {
|
if (battle.battleType === BattleType.TRAINER) {
|
||||||
battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here?
|
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());
|
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
||||||
|
|
||||||
console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats);
|
console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats);
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.scene.getParty().filter(p => p.isShiny()).length === 6) {
|
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);
|
const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter);
|
||||||
battle.mysteryEncounter = newEncounter;
|
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
|
// Load Mystery Encounter Exclamation bubble and sfx
|
||||||
loadEnemyAssets.push(new Promise<void>(resolve => {
|
loadEnemyAssets.push(new Promise<void>(resolve => {
|
||||||
this.scene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav");
|
this.scene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav");
|
||||||
|
@ -176,7 +186,10 @@ export class EncounterPhase extends BattlePhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(loadEnemyAssets).then(() => {
|
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 (e < (battle.double ? 2 : 1)) {
|
||||||
if (battle.battleType === BattleType.WILD) {
|
if (battle.battleType === BattleType.WILD) {
|
||||||
this.scene.field.add(enemyPokemon);
|
this.scene.field.add(enemyPokemon);
|
||||||
|
@ -189,16 +202,15 @@ export class EncounterPhase extends BattlePhase {
|
||||||
} else if (battle.battleType === BattleType.TRAINER) {
|
} else if (battle.battleType === BattleType.TRAINER) {
|
||||||
enemyPokemon.setVisible(false);
|
enemyPokemon.setVisible(false);
|
||||||
this.scene.currentBattle.trainer?.tint(0, 0.5);
|
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) {
|
if (battle.double) {
|
||||||
enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT);
|
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);
|
regenerateModifierPoolThresholds(this.scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD);
|
||||||
this.scene.generateEnemyModifiers();
|
this.scene.generateEnemyModifiers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,21 @@ import { Phase } from "#app/phase";
|
||||||
|
|
||||||
export class PartyExpPhase extends Phase {
|
export class PartyExpPhase extends Phase {
|
||||||
expValue: number;
|
expValue: number;
|
||||||
|
useWaveIndexMultiplier?: boolean;
|
||||||
|
pokemonParticipantIds?: Set<number>;
|
||||||
|
|
||||||
constructor(scene: BattleScene, expValue: number) {
|
constructor(scene: BattleScene, expValue: number, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
this.expValue = expValue;
|
this.expValue = expValue;
|
||||||
|
this.useWaveIndexMultiplier = useWaveIndexMultiplier;
|
||||||
|
this.pokemonParticipantIds = pokemonParticipantIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
this.scene.applyPartyExp(this.expValue);
|
this.scene.applyPartyExp(this.expValue, false, this.useWaveIndexMultiplier, this.pokemonParticipantIds);
|
||||||
|
|
||||||
this.end();
|
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 */
|
/** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */
|
||||||
isExpOnly: boolean;
|
isExpOnly: boolean;
|
||||||
|
|
||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, isExpOnly: boolean = false) {
|
constructor(scene: BattleScene, battlerIndex: BattlerIndex | integer, isExpOnly: boolean = false) {
|
||||||
super(scene, battlerIndex);
|
super(scene, battlerIndex);
|
||||||
|
|
||||||
this.isExpOnly = isExpOnly;
|
this.isExpOnly = isExpOnly;
|
||||||
|
@ -28,7 +28,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||||
this.scene.gameData.gameStats.pokemonDefeated++;
|
this.scene.gameData.gameStats.pokemonDefeated++;
|
||||||
|
|
||||||
const expValue = this.getPokemon().getExpValue();
|
const expValue = this.getPokemon().getExpValue();
|
||||||
this.scene.applyPartyExp(expValue);
|
this.scene.applyPartyExp(expValue, true);
|
||||||
|
|
||||||
if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||||
handleMysteryEncounterVictory(this.scene, false, this.isExpOnly);
|
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].species.speciesId).toBe(Species.SHUCKLE);
|
||||||
expect(enemyField[0].summonData.statStages).toEqual([0, 2, 0, 2, 0, 0, 0]);
|
expect(enemyField[0].summonData.statStages).toEqual([0, 2, 0, 2, 0, 0, 0]);
|
||||||
const shuckleItems = enemyField[0].getHeldItems();
|
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.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.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.APICOT)?.stackCount).toBe(1);
|
||||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.LUM)?.stackCount).toBe(2);
|
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.LUM)?.stackCount).toBe(2);
|
||||||
|
|
Loading…
Reference in New Issue