Skip to content

Commit

Permalink
add unit tests for Dancing Lessons and Part-Timer
Browse files Browse the repository at this point in the history
  • Loading branch information
ImperialSympathizer committed Aug 12, 2024
1 parent f90d8b0 commit 7c9d34a
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
Expand All @@ -23,6 +23,7 @@ import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { BattlerIndex } from "#app/battle";
import { catchPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { PokeballType } from "#enums/pokeball";
import { modifierTypes } from "#app/modifier/modifier-type";

/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:dancingLessons";
Expand Down Expand Up @@ -179,6 +180,7 @@ export const DancingLessonsEncounter: IMysteryEncounter =
ignorePp: true
});

setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
})
.build()
Expand Down Expand Up @@ -223,7 +225,7 @@ export const DancingLessonsEncounter: IMysteryEncounter =
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
selected: [
{
Expand All @@ -242,9 +244,11 @@ export const DancingLessonsEncounter: IMysteryEncounter =
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
// Pokemon and second option selected
// Pokemon and second option selected
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("selectedMove", move.getName());
encounter.misc.selectedMove = move;

return true;
},
};
Expand All @@ -267,9 +271,20 @@ export const DancingLessonsEncounter: IMysteryEncounter =
})
.withOptionPhase(async (scene: BattleScene) => {
// Show the Oricorio a dance, and recruit it
const oricorio = scene.currentBattle.mysteryEncounter.misc.oricorioData.toPokemon(scene);
const encounter = scene.currentBattle.mysteryEncounter;
const oricorio = encounter.misc.oricorioData.toPokemon(scene);
oricorio.passive = true;

// Ensure the Oricorio's moveset gains the Dance move the player used
const move = encounter.misc.selectedMove?.getMove().id;
if (!oricorio.moveset.some(m => m.getMove().id === move)) {
if (oricorio.moveset.length < 4) {
oricorio.moveset.push(new PokemonMove(move));
} else {
oricorio.moveset[3] = new PokemonMove(move);
}
}

transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true);
Expand Down
20 changes: 13 additions & 7 deletions src/data/mystery-encounters/encounters/part-timer-encounter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
Expand Down Expand Up @@ -87,10 +87,10 @@ export const PartTimerEncounter: IMysteryEncounter =
const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());

// Calculate the "baseline" stat value (100 base stat, 31 IVs, neutral nature, same level as pokemon) to compare
// Calculate the "baseline" stat value (90 base stat, 16 IVs, neutral nature, same level as pokemon) to compare
// Resulting money is 2.5 * (% difference from baseline), with minimum of 1 and maximum of 4.
// Calculation from Pokemon.calculateStats
const baselineValue = Math.floor(((2 * 100 + 31) * pokemon.level) * 0.01) + 5;
const baselineValue = Math.floor(((2 * 90 + 16) * pokemon.level) * 0.01) + 5;
const percentDiff = (pokemon.getStat(Stat.SPD) - baselineValue) / baselineValue;
const moneyMultiplier = Math.min(Math.max(2.5 * (1+ percentDiff), 1), 4);

Expand All @@ -104,6 +104,8 @@ export const PartTimerEncounter: IMysteryEncounter =
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
});

setEncounterExp(scene, pokemon.id, 100);

// Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false);
// Play sfx for "working"
Expand Down Expand Up @@ -161,15 +163,15 @@ export const PartTimerEncounter: IMysteryEncounter =
const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());

// Calculate the "baseline" stat value (100 base stat, 31 IVs, neutral nature, same level as pokemon) to compare
// Calculate the "baseline" stat value (75 base stat, 16 IVs, neutral nature, same level as pokemon) to compare
// Resulting money is 2.5 * (% difference from baseline), with minimum of 1 and maximum of 4.
// Calculation from Pokemon.calculateStats
const baselineHp = Math.floor(((2 * 80 + 31) * pokemon.level) * 0.01) + pokemon.level + 10;
const baselineAtkDef = Math.floor(((2 * 80 + 31) * pokemon.level) * 0.01) + 5;
const baselineHp = Math.floor(((2 * 75 + 16) * pokemon.level) * 0.01) + pokemon.level + 10;
const baselineAtkDef = Math.floor(((2 * 75 + 16) * pokemon.level) * 0.01) + 5;
const baselineValue = baselineHp + 1.5 * (baselineAtkDef * 2);
const strongestValue = pokemon.getStat(Stat.HP) + 1.5 * (pokemon.getStat(Stat.ATK) + pokemon.getStat(Stat.DEF));
const percentDiff = (strongestValue - baselineValue) / baselineValue;
const moneyMultiplier = Math.min(Math.max(2.5 * (1+ percentDiff), 1), 4);
const moneyMultiplier = Math.min(Math.max(2.5 * (1 + percentDiff), 1), 4);

encounter.misc = {
moneyMultiplier
Expand All @@ -181,6 +183,8 @@ export const PartTimerEncounter: IMysteryEncounter =
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
});

setEncounterExp(scene, pokemon.id, 100);

// Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false);
// Play sfx for "working"
Expand Down Expand Up @@ -246,6 +250,8 @@ export const PartTimerEncounter: IMysteryEncounter =
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
});

setEncounterExp(scene, selectedPokemon.id, 100);

// Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false);
// Play sfx for "working"
Expand Down
4 changes: 2 additions & 2 deletions src/overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ class DefaultOverrides {
// -------------------------

// 1 to 256, set to null to ignore
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256;
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null;
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.DANCING_LESSONS;
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null;

// -------------------------
// MODIFIER / ITEM OVERRIDES
Expand Down
2 changes: 1 addition & 1 deletion src/phases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ export class EncounterPhase extends BattlePhase {
if (!this.loaded) {
if (battle.battleType === BattleType.TRAINER) {
battle.enemyParty[e] = battle.trainer.genPartyMember(e);
} else if (battle.battleType !== BattleType.MYSTERY_ENCOUNTER) {
} else {
const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true);
battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies));
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
Expand Down
6 changes: 3 additions & 3 deletions src/test/mystery-encounter/encounterTestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler";
* @param secondaryOptionSelect -
* @param isBattle - if selecting option should lead to battle, set to true
*/
export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo: number } = null, isBattle: boolean = false) {
export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo?: number } = null, isBattle: boolean = false) {
vi.spyOn(EncounterPhaseUtils, "selectPokemonForOption");
await runSelectMysteryEncounterOption(game, optionNo, secondaryOptionSelect);

Expand Down Expand Up @@ -65,7 +65,7 @@ export async function runMysteryEncounterToEnd(game: GameManager, optionNo: numb
}
}

export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo: number } = null) {
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo?: number } = null) {
// Handle any eventual queued messages (e.g. weather phase, etc.)
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
Expand Down Expand Up @@ -112,7 +112,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
}
}

async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, optionNo: number) {
async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, optionNo?: number) {
// Handle secondary option selections
const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler;
vi.spyOn(partyUiHandler, "show");
Expand Down
Loading

0 comments on commit 7c9d34a

Please sign in to comment.