[Move] Finish Alluring Voice, Burning Jealousy and Lash Out (#3508)
* Implement Alluring Voice and Burning Jealousy * Fix Alluring Voice and add tests * Replace `BattlerTag.STATS_BOOSTED` with `PokemonTurnData` field * Work around bug with turn data * Remove unused variable * Replace nearby instances of `integer` with `number` * Fix imports * Implement Lash Out * Rename `battleStats(In|De)crease` -> `battleStats(In|De)creased` * Fix copy/paste error Co-authored-by: schmidtc1 <62030095+schmidtc1@users.noreply.github.com> * Update tests --------- Co-authored-by: ElliottSimmonds <simmonds.elliott@yahoo.co.uk> Co-authored-by: schmidtc1 <62030095+schmidtc1@users.noreply.github.com> Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com>
This commit is contained in:
parent
dcb03f4ee9
commit
709066bd1a
|
@ -6037,6 +6037,57 @@ export class DestinyBondAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute to apply a battler tag to the target if they have had their stats boosted this turn.
|
||||||
|
* @extends AddBattlerTagAttr
|
||||||
|
*/
|
||||||
|
export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr {
|
||||||
|
constructor(tag: BattlerTagType) {
|
||||||
|
super(tag, false, false, 2, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} using this move
|
||||||
|
* @param target {@linkcode Pokemon} target of this move
|
||||||
|
* @param move {@linkcode Move} being used
|
||||||
|
* @param {any[]} args N/A
|
||||||
|
* @returns true
|
||||||
|
*/
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (target.turnData.battleStatsIncreased) {
|
||||||
|
super.apply(user, target, move, args);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute to apply a status effect to the target if they have had their stats boosted this turn.
|
||||||
|
* @extends MoveEffectAttr
|
||||||
|
*/
|
||||||
|
export class StatusIfBoostedAttr extends MoveEffectAttr {
|
||||||
|
public effect: StatusEffect;
|
||||||
|
|
||||||
|
constructor(effect: StatusEffect) {
|
||||||
|
super(true, MoveEffectTrigger.HIT);
|
||||||
|
this.effect = effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} using this move
|
||||||
|
* @param target {@linkcode Pokemon} target of this move
|
||||||
|
* @param move {@linkcode Move} N/A
|
||||||
|
* @param {any[]} args N/A
|
||||||
|
* @returns true
|
||||||
|
*/
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (target.turnData.battleStatsIncreased) {
|
||||||
|
target.trySetStatus(this.effect, true, user);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class LastResortAttr extends MoveAttr {
|
export class LastResortAttr extends MoveAttr {
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user: Pokemon, target: Pokemon, move: Move) => {
|
return (user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
|
@ -8694,10 +8745,10 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
|
new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
|
||||||
.attr(StatChangeAttr, BattleStat.SPATK, -1),
|
.attr(StatChangeAttr, BattleStat.SPATK, -1),
|
||||||
new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8)
|
new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8)
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
.attr(StatusIfBoostedAttr, StatusEffect.BURN)
|
||||||
.partial(),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8)
|
new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8)
|
||||||
.partial(),
|
.attr(MovePowerMultiplierAttr, (user, target, move) => user.turnData.battleStatsDecreased ? 2 : 1),
|
||||||
new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8)
|
new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8)
|
||||||
.attr(AttackedByItemAttr)
|
.attr(AttackedByItemAttr)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
|
@ -9146,8 +9197,8 @@ export function initMoves() {
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true)
|
||||||
.target(MoveTarget.NEAR_ALLY),
|
.target(MoveTarget.NEAR_ALLY),
|
||||||
new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
|
new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
|
||||||
.soundBased()
|
.attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED)
|
||||||
.partial(),
|
.soundBased(),
|
||||||
new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9)
|
new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1),
|
.attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1),
|
||||||
new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9)
|
new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9)
|
||||||
|
|
|
@ -4286,7 +4286,7 @@ export interface TurnMove {
|
||||||
targets?: BattlerIndex[];
|
targets?: BattlerIndex[];
|
||||||
result: MoveResult;
|
result: MoveResult;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
turn?: integer;
|
turn?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueuedMove {
|
export interface QueuedMove {
|
||||||
|
@ -4298,17 +4298,17 @@ export interface QueuedMove {
|
||||||
export interface AttackMoveResult {
|
export interface AttackMoveResult {
|
||||||
move: Moves;
|
move: Moves;
|
||||||
result: DamageResult;
|
result: DamageResult;
|
||||||
damage: integer;
|
damage: number;
|
||||||
critical: boolean;
|
critical: boolean;
|
||||||
sourceId: integer;
|
sourceId: number;
|
||||||
sourceBattlerIndex: BattlerIndex;
|
sourceBattlerIndex: BattlerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PokemonSummonData {
|
export class PokemonSummonData {
|
||||||
public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
public battleStats: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
public moveQueue: QueuedMove[] = [];
|
public moveQueue: QueuedMove[] = [];
|
||||||
public disabledMove: Moves = Moves.NONE;
|
public disabledMove: Moves = Moves.NONE;
|
||||||
public disabledTurns: integer = 0;
|
public disabledTurns: number = 0;
|
||||||
public tags: BattlerTag[] = [];
|
public tags: BattlerTag[] = [];
|
||||||
public abilitySuppressed: boolean = false;
|
public abilitySuppressed: boolean = false;
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
|
@ -4318,14 +4318,14 @@ export class PokemonSummonData {
|
||||||
public ability: Abilities = Abilities.NONE;
|
public ability: Abilities = Abilities.NONE;
|
||||||
public gender: Gender;
|
public gender: Gender;
|
||||||
public fusionGender: Gender;
|
public fusionGender: Gender;
|
||||||
public stats: integer[];
|
public stats: number[];
|
||||||
public moveset: (PokemonMove | null)[];
|
public moveset: (PokemonMove | null)[];
|
||||||
// If not initialized this value will not be populated from save data.
|
// If not initialized this value will not be populated from save data.
|
||||||
public types: Type[] = [];
|
public types: Type[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PokemonBattleData {
|
export class PokemonBattleData {
|
||||||
public hitCount: integer = 0;
|
public hitCount: number = 0;
|
||||||
public endured: boolean = false;
|
public endured: boolean = false;
|
||||||
public berriesEaten: BerryType[] = [];
|
public berriesEaten: BerryType[] = [];
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
|
@ -4334,21 +4334,23 @@ export class PokemonBattleData {
|
||||||
|
|
||||||
export class PokemonBattleSummonData {
|
export class PokemonBattleSummonData {
|
||||||
/** The number of turns the pokemon has passed since entering the battle */
|
/** The number of turns the pokemon has passed since entering the battle */
|
||||||
public turnCount: integer = 1;
|
public turnCount: number = 1;
|
||||||
/** The list of moves the pokemon has used since entering the battle */
|
/** The list of moves the pokemon has used since entering the battle */
|
||||||
public moveHistory: TurnMove[] = [];
|
public moveHistory: TurnMove[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PokemonTurnData {
|
export class PokemonTurnData {
|
||||||
public flinched: boolean;
|
public flinched: boolean = false;
|
||||||
public acted: boolean;
|
public acted: boolean = false;
|
||||||
public hitCount: integer;
|
public hitCount: number;
|
||||||
public hitsLeft: integer;
|
public hitsLeft: number;
|
||||||
public damageDealt: integer = 0;
|
public damageDealt: number = 0;
|
||||||
public currDamageDealt: integer = 0;
|
public currDamageDealt: number = 0;
|
||||||
public damageTaken: integer = 0;
|
public damageTaken: number = 0;
|
||||||
public attacksReceived: AttackMoveResult[] = [];
|
public attacksReceived: AttackMoveResult[] = [];
|
||||||
public order: number;
|
public order: number;
|
||||||
|
public battleStatsIncreased: boolean = false;
|
||||||
|
public battleStatsDecreased: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AiType {
|
export enum AiType {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import BattleScene from "#app/battle-scene.js";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { BattlerIndex } from "#app/battle.js";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { applyPreStatChangeAbAttrs, ProtectStatAbAttr, applyAbAttrs, StatChangeMultiplierAbAttr, StatChangeCopyAbAttr, applyPostStatChangeAbAttrs, PostStatChangeAbAttr } from "#app/data/ability.js";
|
import { applyAbAttrs, applyPostStatChangeAbAttrs, applyPreStatChangeAbAttrs, PostStatChangeAbAttr, ProtectStatAbAttr, StatChangeCopyAbAttr, StatChangeMultiplierAbAttr } from "#app/data/ability";
|
||||||
import { MistTag, ArenaTagSide } from "#app/data/arena-tag.js";
|
import { ArenaTagSide, MistTag } from "#app/data/arena-tag";
|
||||||
import { BattleStat, getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js";
|
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat";
|
||||||
import Pokemon from "#app/field/pokemon.js";
|
import Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier.js";
|
import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial.js";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Utils from "#app/utils.js";
|
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
|
||||||
export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void;
|
export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void;
|
||||||
|
@ -72,7 +72,7 @@ export class StatChangePhase extends PokemonPhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
const battleStats = this.getPokemon().summonData.battleStats;
|
const battleStats = this.getPokemon().summonData.battleStats;
|
||||||
const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]);
|
const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats[stat] + levels.value, 6) : Math.max(battleStats[stat] + levels.value, -6)) - battleStats[stat]);
|
||||||
|
|
||||||
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
||||||
|
|
||||||
|
@ -85,6 +85,20 @@ export class StatChangePhase extends PokemonPhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const stat of filteredStats) {
|
for (const stat of filteredStats) {
|
||||||
|
if (levels.value > 0 && pokemon.summonData.battleStats[stat] < 6) {
|
||||||
|
if (!pokemon.turnData) {
|
||||||
|
// Temporary fix for missing turn data struct on turn 1
|
||||||
|
pokemon.resetTurnData();
|
||||||
|
}
|
||||||
|
pokemon.turnData.battleStatsIncreased = true;
|
||||||
|
} else if (levels.value < 0 && pokemon.summonData.battleStats[stat] > -6) {
|
||||||
|
if (!pokemon.turnData) {
|
||||||
|
// Temporary fix for missing turn data struct on turn 1
|
||||||
|
pokemon.resetTurnData();
|
||||||
|
}
|
||||||
|
pokemon.turnData.battleStatsDecreased = true;
|
||||||
|
}
|
||||||
|
|
||||||
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6);
|
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
|
import { BerryPhase } from "#app/phases/berry-phase";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
describe("Moves - Alluring Voice", () => {
|
||||||
|
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
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.ICE_SCALES)
|
||||||
|
.enemyMoveset(Array(4).fill(Moves.HOWL))
|
||||||
|
.startingLevel(10)
|
||||||
|
.enemyLevel(10)
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.moveset([Moves.ALLURING_VOICE]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should confuse the opponent if their stats were raised", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.ALLURING_VOICE);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.phaseInterceptor.to(BerryPhase);
|
||||||
|
|
||||||
|
expect(enemy.getTag(BattlerTagType.CONFUSED)?.tagType).toBe("CONFUSED");
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
|
@ -0,0 +1,103 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
describe("Moves - Burning Jealousy", () => {
|
||||||
|
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
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.ICE_SCALES)
|
||||||
|
.enemyMoveset(Array(4).fill(Moves.HOWL))
|
||||||
|
.startingLevel(10)
|
||||||
|
.enemyLevel(10)
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.moveset([Moves.BURNING_JEALOUSY, Moves.GROWL]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should burn the opponent if their stats were raised", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.BURNING_JEALOUSY);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(enemy.status?.effect).toBe(StatusEffect.BURN);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("should still burn the opponent if their stats were both raised and lowered in the same turn", async () => {
|
||||||
|
game.override
|
||||||
|
.starterSpecies(0)
|
||||||
|
.battleType("double");
|
||||||
|
await game.classicMode.startBattle([Species.FEEBAS, Species.ABRA]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.BURNING_JEALOUSY);
|
||||||
|
game.move.select(Moves.GROWL, 1);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(enemy.status?.effect).toBe(StatusEffect.BURN);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("should ignore stats raised by imposter", async () => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.DITTO)
|
||||||
|
.enemyAbility(Abilities.IMPOSTER)
|
||||||
|
.enemyMoveset(SPLASH_ONLY);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.BURNING_JEALOUSY);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(enemy.status?.effect).toBeUndefined();
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it.skip("should ignore weakness policy", async () => { // TODO: Make this test if WP is implemented
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("should be boosted by Sheer Force even if opponent didn't raise stats", async () => {
|
||||||
|
game.override
|
||||||
|
.ability(Abilities.SHEER_FORCE)
|
||||||
|
.enemyMoveset(SPLASH_ONLY);
|
||||||
|
vi.spyOn(allMoves[Moves.BURNING_JEALOUSY], "calculateBattlePower");
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.BURNING_JEALOUSY);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(allMoves[Moves.BURNING_JEALOUSY].calculateBattlePower).toHaveReturnedWith(allMoves[Moves.BURNING_JEALOUSY].power * 5461 / 4096);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
describe("Moves - Lash Out", () => {
|
||||||
|
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
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.FUR_COAT)
|
||||||
|
.enemyMoveset(Array(4).fill(Moves.GROWL))
|
||||||
|
.startingLevel(10)
|
||||||
|
.enemyLevel(10)
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.moveset([Moves.LASH_OUT]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deal double damage if the user's stats were lowered this turn", async () => {
|
||||||
|
vi.spyOn(allMoves[Moves.LASH_OUT], "calculateBattlePower");
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.LASH_OUT);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(allMoves[Moves.LASH_OUT].calculateBattlePower).toHaveReturnedWith(150);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
Loading…
Reference in New Issue