[Test] Added learn move utility function & level cap overrides (#5058)
* Added learn move utility function * Added utility functions * add another test * Update overrides.ts * Update moveHelper.ts * Update overridesHelper.ts --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: damocleas <damocleas25@gmail.com>
This commit is contained in:
parent
9c29cdc63d
commit
747656e8df
|
@ -1843,8 +1843,10 @@ export default class BattleScene extends SceneBase {
|
|||
this.currentBattle.battleScore += Math.ceil(scoreIncrease);
|
||||
}
|
||||
|
||||
getMaxExpLevel(ignoreLevelCap?: boolean): integer {
|
||||
if (ignoreLevelCap) {
|
||||
getMaxExpLevel(ignoreLevelCap: boolean = false): integer {
|
||||
if (Overrides.LEVEL_CAP_OVERRIDE > 0) {
|
||||
return Overrides.LEVEL_CAP_OVERRIDE;
|
||||
} else if (ignoreLevelCap || Overrides.LEVEL_CAP_OVERRIDE < 0) {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
|
||||
|
|
|
@ -2391,8 +2391,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.battleInfo.toggleFlyout(visible);
|
||||
}
|
||||
|
||||
addExp(exp: integer) {
|
||||
const maxExpLevel = globalScene.getMaxExpLevel();
|
||||
/**
|
||||
* Adds experience to this PlayerPokemon, subject to wave based level caps.
|
||||
* @param exp The amount of experience to add
|
||||
* @param ignoreLevelCap Whether to ignore level caps when adding experience (defaults to false)
|
||||
*/
|
||||
addExp(exp: integer, ignoreLevelCap: boolean = false) {
|
||||
const maxExpLevel = globalScene.getMaxExpLevel(ignoreLevelCap);
|
||||
const initialExp = this.exp;
|
||||
this.exp += exp;
|
||||
while (this.level < maxExpLevel && this.exp >= getLevelTotalExp(this.level + 1, this.species.growthRate)) {
|
||||
|
|
|
@ -63,8 +63,11 @@ class DefaultOverrides {
|
|||
readonly STARTING_WAVE_OVERRIDE: number = 0;
|
||||
readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
|
||||
readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null;
|
||||
/** Multiplies XP gained by this value including 0. Set to null to ignore the override */
|
||||
/** Multiplies XP gained by this value including 0. Set to null to ignore the override. */
|
||||
readonly XP_MULTIPLIER_OVERRIDE: number | null = null;
|
||||
/** Sets the level cap to this number during experience gain calculations. Set to `0` to disable override & use normal wave-based level caps,
|
||||
or any negative number to set it to 9 quadrillion (effectively disabling it). */
|
||||
readonly LEVEL_CAP_OVERRIDE: number = 0;
|
||||
readonly NEVER_CRIT_OVERRIDE: boolean = false;
|
||||
/** default 1000 */
|
||||
readonly STARTING_MONEY_OVERRIDE: number = 0;
|
||||
|
|
|
@ -4,6 +4,8 @@ import GameManager from "#test/utils/gameManager";
|
|||
import { Species } from "#enums/species";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { Button } from "#app/enums/buttons";
|
||||
|
||||
describe("Learn Move Phase", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -26,7 +28,7 @@ describe("Learn Move Phase", () => {
|
|||
|
||||
it("If Pokemon has less than 4 moves, its newest move will be added to the lowest empty index", async () => {
|
||||
game.override.moveset([ Moves.SPLASH ]);
|
||||
await game.startBattle([ Species.BULBASAUR ]);
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const pokemon = game.scene.getPlayerPokemon()!;
|
||||
const newMovePos = pokemon?.getMoveset().length;
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
@ -36,12 +38,113 @@ describe("Learn Move Phase", () => {
|
|||
const levelReq = levelMove[0];
|
||||
const levelMoveId = levelMove[1];
|
||||
expect(pokemon.level).toBeGreaterThanOrEqual(levelReq);
|
||||
expect(pokemon?.getMoveset()[newMovePos]?.moveId).toBe(levelMoveId);
|
||||
expect(pokemon?.moveset[newMovePos]?.moveId).toBe(levelMoveId);
|
||||
});
|
||||
|
||||
it("If a pokemon has 4 move slots filled, the chosen move will be deleted and replaced", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const bulbasaur = game.scene.getPlayerPokemon()!;
|
||||
const prevMoveset = [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ];
|
||||
const moveSlotNum = 3;
|
||||
|
||||
game.move.changeMoveset(bulbasaur, prevMoveset);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
|
||||
// queue up inputs to confirm dialog boxes
|
||||
game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => {
|
||||
game.scene.ui.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => {
|
||||
for (let x = 0; x < moveSlotNum; x++) {
|
||||
game.scene.ui.processInput(Button.DOWN);
|
||||
}
|
||||
game.scene.ui.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.to(LearnMovePhase);
|
||||
|
||||
const levelMove = bulbasaur.getLevelMoves(5)[0];
|
||||
const levelReq = levelMove[0];
|
||||
const levelMoveId = levelMove[1];
|
||||
expect(bulbasaur.level).toBeGreaterThanOrEqual(levelReq);
|
||||
// Check each of mr mime's moveslots to make sure the changed move (and ONLY the changed move) is different
|
||||
bulbasaur.getMoveset().forEach((move, index) => {
|
||||
const expectedMove: Moves = (index === moveSlotNum ? levelMoveId : prevMoveset[index]);
|
||||
expect(move?.moveId).toBe(expectedMove);
|
||||
});
|
||||
});
|
||||
|
||||
it("selecting the newly deleted move will reject it and keep old moveset", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const bulbasaur = game.scene.getPlayerPokemon()!;
|
||||
const prevMoveset = [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ];
|
||||
|
||||
game.move.changeMoveset(bulbasaur, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
|
||||
// queue up inputs to confirm dialog boxes
|
||||
game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => {
|
||||
game.scene.ui.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => {
|
||||
for (let x = 0; x < 4; x++) {
|
||||
game.scene.ui.processInput(Button.DOWN); // moves down 4 times to the 5th move slot
|
||||
}
|
||||
game.scene.ui.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => {
|
||||
game.scene.ui.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.to(LearnMovePhase);
|
||||
|
||||
const levelReq = bulbasaur.getLevelMoves(5)[0][0];
|
||||
expect(bulbasaur.level).toBeGreaterThanOrEqual(levelReq);
|
||||
expect(bulbasaur.getMoveset().map(m => m?.moveId)).toEqual(prevMoveset);
|
||||
});
|
||||
|
||||
it("macro should add moves in free slots normally", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const bulbasaur = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.changeMoveset(bulbasaur, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID ]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.move.learnMove(Moves.SACRED_FIRE, 0, 1);
|
||||
expect(bulbasaur.getMoveset().map(m => m?.moveId)).toEqual([ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.SACRED_FIRE ]);
|
||||
|
||||
});
|
||||
|
||||
it("macro should replace moves", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const bulbasaur = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.changeMoveset(bulbasaur, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.move.learnMove(Moves.SACRED_FIRE, 0, 1);
|
||||
expect(bulbasaur.getMoveset().map(m => m?.moveId)).toEqual([ Moves.SPLASH, Moves.SACRED_FIRE, Moves.ACID, Moves.VINE_WHIP ]);
|
||||
|
||||
});
|
||||
|
||||
it("macro should allow for cancelling move learning", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
const bulbasaur = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.changeMoveset(bulbasaur, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.move.learnMove(Moves.SACRED_FIRE, 0, 4);
|
||||
expect(bulbasaur.getMoveset().map(m => m?.moveId)).toEqual([ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]);
|
||||
|
||||
});
|
||||
|
||||
it("macro works on off-field party members", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR, Species.SQUIRTLE ]);
|
||||
const squirtle = game.scene.getPlayerParty()[1]!;
|
||||
|
||||
game.move.changeMoveset(squirtle, [ Moves.SPLASH, Moves.WATER_GUN, Moves.FREEZE_DRY, Moves.GROWL ]);
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.move.learnMove(Moves.SHELL_SMASH, 1, 0);
|
||||
expect(squirtle.getMoveset().map(m => m?.moveId)).toEqual([ Moves.SHELL_SMASH, Moves.WATER_GUN, Moves.FREEZE_DRY, Moves.GROWL ]);
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Future Tests:
|
||||
* If a Pokemon has four moves, the user can specify an old move to be forgotten and a new move will take its place.
|
||||
* If a Pokemon has four moves, the user can reject the new move, keeping the moveset the same.
|
||||
*/
|
||||
});
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import type { BattlerIndex } from "#app/battle";
|
||||
import { Button } from "#app/enums/buttons";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import Overrides from "#app/overrides";
|
||||
import type { CommandPhase } from "#app/phases/command-phase";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
|
@ -75,9 +77,10 @@ export class MoveHelper extends GameManagerHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Used when the normal moveset override can't be used (such as when it's necessary to check updated properties of the moveset).
|
||||
* @param pokemon - The pokemon being modified
|
||||
* @param moveset - The moveset to use
|
||||
* Changes a pokemon's moveset to the given move(s).
|
||||
* Used when the normal moveset override can't be used (such as when it's necessary to check or update properties of the moveset).
|
||||
* @param pokemon - The {@linkcode Pokemon} being modified
|
||||
* @param moveset - The {@linkcode Moves} (single or array) to change the Pokemon's moveset to
|
||||
*/
|
||||
public changeMoveset(pokemon: Pokemon, moveset: Moves | Moves[]): void {
|
||||
if (!Array.isArray(moveset)) {
|
||||
|
@ -90,4 +93,40 @@ export class MoveHelper extends GameManagerHelper {
|
|||
const movesetStr = moveset.map((moveId) => Moves[moveId]).join(", ");
|
||||
console.log(`Pokemon ${pokemon.species.name}'s moveset manually set to ${movesetStr} (=[${moveset.join(", ")}])!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates learning a move for a player pokemon.
|
||||
* @param move The {@linkcode Moves} being learnt
|
||||
* @param partyIndex The party position of the {@linkcode PlayerPokemon} learning the move (defaults to 0)
|
||||
* @param moveSlotIndex The INDEX (0-4) of the move slot to replace if existent move slots are full;
|
||||
* defaults to 0 (first slot) and 4 aborts the procedure
|
||||
* @returns a promise that resolves once the move has been successfully learnt
|
||||
*/
|
||||
public async learnMove(move: Moves | integer, partyIndex: integer = 0, moveSlotIndex: integer = 0) {
|
||||
return new Promise<void>(async (resolve, reject) => {
|
||||
this.game.scene.pushPhase(new LearnMovePhase(partyIndex, move));
|
||||
|
||||
// if slots are full, queue up inputs to replace existing moves
|
||||
if (this.game.scene.getPlayerParty()[partyIndex].moveset.filter(m => m).length === 4) {
|
||||
this.game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => {
|
||||
this.game.scene.ui.processInput(Button.ACTION); // "Should a move be forgotten and replaced with XXX?"
|
||||
});
|
||||
this.game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => {
|
||||
for (let x = 0; x < (moveSlotIndex ?? 0); x++) {
|
||||
this.game.scene.ui.processInput(Button.DOWN); // Scrolling in summary pane to move position
|
||||
}
|
||||
this.game.scene.ui.processInput(Button.ACTION);
|
||||
if (moveSlotIndex === 4) {
|
||||
this.game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => {
|
||||
this.game.scene.ui.processInput(Button.ACTION); // "Give up on learning XXX?"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await this.game.phaseInterceptor.to(LearnMovePhase).catch(e => reject(e));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,6 +71,26 @@ export class OverridesHelper extends GameManagerHelper {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the wave level cap
|
||||
* @param cap the level cap value to set; 0 uses normal level caps and negative values
|
||||
* disable it completely
|
||||
* @returns `this`
|
||||
*/
|
||||
public levelCap(cap: number): this {
|
||||
vi.spyOn(Overrides, "LEVEL_CAP_OVERRIDE", "get").mockReturnValue(cap);
|
||||
let capStr: string;
|
||||
if (cap > 0) {
|
||||
capStr = `Level cap set to ${cap}!`;
|
||||
} else if (cap < 0) {
|
||||
capStr = "Level cap disabled!";
|
||||
} else {
|
||||
capStr = "Level cap reset to default value for wave.";
|
||||
}
|
||||
this.log(capStr);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the player (pokemon) starting held items
|
||||
* @param items the items to hold
|
||||
|
|
Loading…
Reference in New Issue