mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-09 08:59:29 +02:00
Fixed tests and documentation
This commit is contained in:
parent
944fc14fb6
commit
b377581075
@ -3157,9 +3157,13 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
||||
|
||||
// Display the move animation to foresee an attack
|
||||
globalScene.phaseManager.unshiftNew("MoveAnimPhase", new MoveChargeAnim(this.chargeAnim, move.id, user));
|
||||
globalScene.phaseManager.queueMessage(i18next.t(this.chargeText,
|
||||
// uncomment if any new delayed moves actually use target in the move text.
|
||||
{pokemonName: getPokemonNameWithAffix(user)/*, targetName: getPokemonNameWithAffix(target) */}))
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
this.chargeText,
|
||||
// uncomment if any new delayed moves actually use target in the move text.
|
||||
{pokemonName: getPokemonNameWithAffix(user)/*, targetName: getPokemonNameWithAffix(target) */}
|
||||
)
|
||||
)
|
||||
|
||||
user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode: useMode, turn: globalScene.currentBattle.turn})
|
||||
|
||||
@ -3180,10 +3184,15 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/** Attribute to queue a {@linkcode WishTag} to activate in 2 turns. */
|
||||
export class WishAttr extends MoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, _move: Move): boolean {
|
||||
globalScene.arena.positionalTagManager.addTag<PositionalTagType.WISH>({tagType: PositionalTagType.WISH, sourceId: user.id, healHp: toDmgValue(user.getMaxHp() / 2), targetIndex: target.getBattlerIndex(),
|
||||
globalScene.arena.positionalTagManager.addTag<PositionalTagType.WISH>({
|
||||
tagType: PositionalTagType.WISH,
|
||||
healHp: toDmgValue(user.getMaxHp() / 2),
|
||||
targetIndex: target.getBattlerIndex(),
|
||||
turnCount: 2,
|
||||
pokemonName: getPokemonNameWithAffix(user),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
@ -12,14 +12,11 @@ import type { Pokemon } from "#field/pokemon";
|
||||
import i18next from "i18next";
|
||||
|
||||
/**
|
||||
* Baseline arguments used to construct all {@linkcode PositionalTag}s.
|
||||
* Baseline arguments used to construct all {@linkcode PositionalTag}s,
|
||||
* the contents of which are serialized and used to construct new tags. \
|
||||
* Does not contain the `tagType` parameter (which is used to select the proper class constructor to use).
|
||||
*/
|
||||
export interface PositionalTagBaseArgs {
|
||||
/**
|
||||
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created the effect.
|
||||
*/
|
||||
sourceId: number;
|
||||
/**
|
||||
* The number of turns remaining until activation. \
|
||||
* Decremented by 1 at the end of each turn until reaching 0, at which point it will {@linkcode trigger} and be removed.
|
||||
@ -42,12 +39,10 @@ export interface PositionalTagBaseArgs {
|
||||
export abstract class PositionalTag implements PositionalTagBaseArgs {
|
||||
public abstract readonly tagType: PositionalTagType;
|
||||
// These arguments have to be public to implement the interface, but are functionally private.
|
||||
public sourceId: number;
|
||||
public turnCount: number;
|
||||
public targetIndex: BattlerIndex;
|
||||
|
||||
constructor({ sourceId, turnCount, targetIndex }: PositionalTagBaseArgs) {
|
||||
this.sourceId = sourceId;
|
||||
constructor({ turnCount, targetIndex }: PositionalTagBaseArgs) {
|
||||
this.turnCount = turnCount;
|
||||
this.targetIndex = targetIndex;
|
||||
}
|
||||
@ -79,6 +74,10 @@ export abstract class PositionalTag implements PositionalTagBaseArgs {
|
||||
}
|
||||
|
||||
interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
||||
/**
|
||||
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created this effect.
|
||||
*/
|
||||
sourceId: number;
|
||||
/** The {@linkcode MoveId} that created this attack. */
|
||||
sourceMove: MoveId;
|
||||
}
|
||||
@ -91,9 +90,11 @@ interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
||||
export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs {
|
||||
public override readonly tagType = PositionalTagType.DELAYED_ATTACK;
|
||||
public sourceMove: MoveId;
|
||||
public sourceId: number;
|
||||
|
||||
constructor({ sourceId, turnCount, targetIndex, sourceMove }: DelayedAttackArgs) {
|
||||
super({ sourceId, turnCount, targetIndex });
|
||||
super({ turnCount, targetIndex });
|
||||
this.sourceId = sourceId;
|
||||
this.sourceMove = sourceMove;
|
||||
}
|
||||
|
||||
@ -130,6 +131,10 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
|
||||
interface WishArgs extends PositionalTagBaseArgs {
|
||||
/** The amount of {@linkcode Stat.HP | HP} to heal; set to 50% of the user's max HP during move usage. */
|
||||
healHp: number;
|
||||
/**
|
||||
* The name of the {@linkcode Pokemon} having created the tag..
|
||||
*/
|
||||
pokemonName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,17 +143,29 @@ interface WishArgs extends PositionalTagBaseArgs {
|
||||
export class WishTag extends PositionalTag implements WishArgs {
|
||||
public override readonly tagType = PositionalTagType.WISH;
|
||||
|
||||
readonly pokemonName: string;
|
||||
|
||||
public healHp: number;
|
||||
constructor({ sourceId, turnCount, targetIndex, healHp }: WishArgs) {
|
||||
super({ sourceId, turnCount, targetIndex });
|
||||
constructor({ turnCount, targetIndex, healHp, pokemonName }: WishArgs) {
|
||||
super({ turnCount, targetIndex });
|
||||
this.healHp = healHp;
|
||||
this.pokemonName = pokemonName;
|
||||
}
|
||||
|
||||
public trigger(): void {
|
||||
// TODO: Rename this locales key - wish shows a message on REMOVAL, dumbass
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:wishTagOnAdd", {
|
||||
pokemonName: this.pokemonName,
|
||||
}),
|
||||
);
|
||||
|
||||
globalScene.phaseManager.unshiftNew("PokemonHealPhase", this.targetIndex, this.healHp, null, true, false);
|
||||
}
|
||||
|
||||
public shouldDisappear(): boolean {
|
||||
// Disappear if no target.
|
||||
// The source need not exist at the time of activation (since all we need is a simple message)
|
||||
return !!this.getTarget();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { MoveFlags } from "#enums/MoveFlags";
|
||||
import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MoveResult } from "#enums/move-result";
|
||||
import { isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode";
|
||||
import { isReflected, MoveUseMode } from "#enums/move-use-mode";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import type { Pokemon } from "#field/pokemon";
|
||||
import {
|
||||
@ -244,22 +244,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
globalScene.currentBattle.lastPlayerInvolved = this.fieldIndex;
|
||||
}
|
||||
|
||||
const isDelayedAttack = this.move.hasAttr("DelayedAttackAttr");
|
||||
/** If the user was somehow removed from the field and it's not a delayed attack, end this phase */
|
||||
if (!user.isOnField()) {
|
||||
if (!isDelayedAttack) {
|
||||
super.end();
|
||||
return;
|
||||
}
|
||||
if (!user.scene) {
|
||||
/*
|
||||
* This happens if the Pokemon that used the delayed attack gets caught and released
|
||||
* on the turn the attack would have triggered. Having access to the global scene
|
||||
* in the future may solve this entirely, so for now we just cancel the hit
|
||||
*/
|
||||
super.end();
|
||||
return;
|
||||
}
|
||||
if (!user.scene) {
|
||||
/*
|
||||
* This happens if the Pokemon that used the delayed attack gets caught and released
|
||||
* on the turn the attack would have triggered. Having access to the global scene
|
||||
* in the future may solve this entirely, so for now we just cancel the hit
|
||||
*/
|
||||
console.warn("User scene bye bye bye skibidi rizz");
|
||||
super.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const move = this.move;
|
||||
@ -270,18 +263,12 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
*/
|
||||
const overridden = new BooleanHolder(false);
|
||||
|
||||
console.log(this.useMode);
|
||||
// Apply effects to override a move effect.
|
||||
// Assuming single target here works as this is (currently)
|
||||
// only used for Future Sight, calling and Pledge moves.
|
||||
// TODO: change if any other move effect overrides are introduced
|
||||
applyMoveAttrs(
|
||||
"OverrideMoveEffectAttr",
|
||||
user,
|
||||
this.getFirstTarget() ?? null,
|
||||
move,
|
||||
overridden,
|
||||
isVirtual(this.useMode),
|
||||
);
|
||||
applyMoveAttrs("OverrideMoveEffectAttr", user, this.getFirstTarget() ?? null, move, overridden, this.useMode);
|
||||
|
||||
// If other effects were overriden, stop this phase before they can be applied
|
||||
if (overridden.value) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { DelayedAttackTag } from "#app/data/positional-tags/positional-tag";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||
import { allMoves } from "#data/data-lists";
|
||||
@ -8,6 +7,7 @@ import { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MoveResult } from "#enums/move-result";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { GameManager } from "#test/testUtils/gameManager";
|
||||
import i18next from "i18next";
|
||||
@ -68,7 +68,9 @@ describe("Moves - Delayed Attacks", () => {
|
||||
* @param numAttacks - The number of delayed attacks that should be queued; default `1`
|
||||
*/
|
||||
function expectFutureSightActive(numAttacks = 1) {
|
||||
const delayedAttacks = game.scene.arena.positionalTagManager["tags"].filter(t => t instanceof DelayedAttackTag)!;
|
||||
const delayedAttacks = game.scene.arena.positionalTagManager["tags"].filter(
|
||||
t => t.tagType === PositionalTagType.DELAYED_ATTACK,
|
||||
);
|
||||
expect(delayedAttacks).toHaveLength(numAttacks);
|
||||
}
|
||||
|
||||
@ -171,8 +173,8 @@ describe("Moves - Delayed Attacks", () => {
|
||||
|
||||
expectFutureSightActive(4);
|
||||
|
||||
game.move.use(MoveId.TAILWIND);
|
||||
game.move.use(MoveId.COTTON_SPORE);
|
||||
game.move.use(MoveId.TAILWIND, BattlerIndex.PLAYER);
|
||||
game.move.use(MoveId.COTTON_SPORE, BattlerIndex.PLAYER_2);
|
||||
await passTurns(1, false);
|
||||
|
||||
expect(game.field.getSpeedOrder()).not.toEqual(usageOrder);
|
||||
@ -251,12 +253,10 @@ describe("Moves - Delayed Attacks", () => {
|
||||
|
||||
expectFutureSightActive(1);
|
||||
|
||||
await passTurns(1);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.killPokemon(enemy2);
|
||||
await game.toNextTurn();
|
||||
await passTurns(2);
|
||||
|
||||
expectFutureSightActive(0);
|
||||
expect(enemy1.hp).toBe(enemy1.getMaxHp());
|
||||
expect(game.textInterceptor.logs).not.toContain(
|
||||
i18next.t("moveTriggers:tookMoveAttack", {
|
||||
@ -279,7 +279,8 @@ describe("Moves - Delayed Attacks", () => {
|
||||
|
||||
expectFutureSightActive(1);
|
||||
|
||||
await passTurns(1);
|
||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||
await game.move.forceEnemyMove(MoveId.ELECTRIFY, BattlerIndex.PLAYER);
|
||||
|
129
test/moves/wish.test.ts
Normal file
129
test/moves/wish.test.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { GameManager } from "#test/testUtils/gameManager";
|
||||
import { toDmgValue } from "#utils/common";
|
||||
import i18next from "i18next";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Move - Wish", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100);
|
||||
});
|
||||
|
||||
/**
|
||||
* Expect that wish is active with the specified number of attacks.
|
||||
* @param numAttacks - The number of wish instances that should be queued; default `1`
|
||||
*/
|
||||
function expectWishActive(numAttacks = 1) {
|
||||
const wishes = game.scene.arena.positionalTagManager["tags"].filter(t => t.tagType === PositionalTagType.WISH);
|
||||
expect(wishes).toHaveLength(numAttacks);
|
||||
}
|
||||
|
||||
it("should heal the Pokemon in the current slot for 50% of the user's maximum HP", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.ALOMOMOLA, SpeciesId.BLISSEY]);
|
||||
|
||||
const [alomomola, blissey] = game.scene.getPlayerParty();
|
||||
alomomola.hp = 1;
|
||||
blissey.hp = 1;
|
||||
|
||||
game.move.use(MoveId.WISH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expectWishActive();
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expectWishActive(0);
|
||||
expect(game.textInterceptor.logs).toContain(
|
||||
i18next.t("arenaTag:wishTagOnAdd", {
|
||||
pokemonName: getPokemonNameWithAffix(blissey),
|
||||
}),
|
||||
);
|
||||
expect(alomomola.hp).toBe(1);
|
||||
expect(blissey.hp).toBe(toDmgValue(alomomola.getMaxHp() / 2) + 1);
|
||||
});
|
||||
|
||||
it("should function independently of Future Sight", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.ALOMOMOLA, SpeciesId.BLISSEY]);
|
||||
|
||||
const [alomomola, blissey] = game.scene.getPlayerParty();
|
||||
alomomola.hp = 1;
|
||||
blissey.hp = 1;
|
||||
|
||||
game.move.use(MoveId.WISH);
|
||||
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.toNextTurn();
|
||||
|
||||
expectWishActive(1);
|
||||
});
|
||||
|
||||
it("should work in double battles and triggerin order of creation", async () => {
|
||||
game.override.battleStyle("double");
|
||||
await game.classicMode.startBattle([SpeciesId.ALOMOMOLA, SpeciesId.BLISSEY]);
|
||||
|
||||
const [alomomola, blissey] = game.scene.getPlayerParty();
|
||||
alomomola.hp = 1;
|
||||
blissey.hp = 1;
|
||||
|
||||
const oldOrder = game.field.getSpeedOrder();
|
||||
|
||||
game.move.use(MoveId.WISH, BattlerIndex.PLAYER);
|
||||
game.move.use(MoveId.WISH, BattlerIndex.PLAYER_2);
|
||||
await game.toNextTurn();
|
||||
|
||||
expectWishActive(2);
|
||||
|
||||
// Lower speed to change turn order
|
||||
alomomola.setStatStage(Stat.SPD, 6);
|
||||
blissey.setStatStage(Stat.SPD, -6);
|
||||
|
||||
const newOrder = game.field.getSpeedOrder();
|
||||
expect(newOrder).not.toEqual(oldOrder);
|
||||
|
||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
await game.phaseInterceptor.to("PositionalTagPhase");
|
||||
|
||||
// Both wishes have activated and added healing phases
|
||||
expectWishActive(0);
|
||||
|
||||
const healPhases = game.scene.phaseManager.phaseQueue.filter(p => p.is("PokemonHealPhase"));
|
||||
expect(healPhases).toHaveLength(4);
|
||||
expect(healPhases.map(php => php["battlerIndex"])).toEqual(oldOrder);
|
||||
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(alomomola.hp).toBe(toDmgValue(alomomola.getMaxHp() / 2) + 1);
|
||||
expect(blissey.hp).toBe(toDmgValue(blissey.getMaxHp() / 2) + 1);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user