Compare commits
6 Commits
e9d17f0605
...
1487d7f51c
Author | SHA1 | Date | |
---|---|---|---|
|
1487d7f51c | ||
|
0241a0a086 | ||
|
3a167610cf | ||
|
6b21a777a1 | ||
|
0e59e74197 | ||
|
cd489c6a60 |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 8.3 KiB |
@ -3784,6 +3784,30 @@ export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the power of Tera Blast if the user is Terastallized into Stellar type
|
||||||
|
* @extends VariablePowerAttr
|
||||||
|
*/
|
||||||
|
export class TeraBlastPowerAttr extends VariablePowerAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} Pokemon using the move
|
||||||
|
* @param target {@linkcode Pokemon} N/A
|
||||||
|
* @param move {@linkcode Move} {@linkcode Move.TERA_BLAST}
|
||||||
|
* @param {any[]} args N/A
|
||||||
|
* @returns true or false
|
||||||
|
*/
|
||||||
|
const power = args[0] as Utils.NumberHolder;
|
||||||
|
if (user.isTerastallized() && move.type === Type.STELLAR) {
|
||||||
|
//200 instead of 100 to reflect lack of stellar being 2x dmg on any type
|
||||||
|
power.value = 200;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the move category to status when used on the ally
|
* Change the move category to status when used on the ally
|
||||||
* @extends VariableMoveCategoryAttr
|
* @extends VariableMoveCategoryAttr
|
||||||
@ -4037,6 +4061,28 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the type of Tera Blast to match the user's tera type
|
||||||
|
* @extends VariableMoveTypeAttr
|
||||||
|
*/
|
||||||
|
export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} the user's type is checked
|
||||||
|
* @param target {@linkcode Pokemon} N/A
|
||||||
|
* @param move {@linkcode Move} {@linkcode Move.TeraBlastTypeAttr}
|
||||||
|
* @param {any[]} args N/A
|
||||||
|
* @returns true or false
|
||||||
|
*/
|
||||||
|
if (user.isTerastallized()) {
|
||||||
|
move.type = user.getTeraType(); //changes move type to tera type
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class MatchUserTypeAttr extends VariableMoveTypeAttr {
|
export class MatchUserTypeAttr extends VariableMoveTypeAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const userTypes = user.getTypes(true);
|
const userTypes = user.getTypes(true);
|
||||||
@ -8791,7 +8837,10 @@ export function initMoves() {
|
|||||||
End Unused */
|
End Unused */
|
||||||
new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
|
new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
|
||||||
.attr(TeraBlastCategoryAttr)
|
.attr(TeraBlastCategoryAttr)
|
||||||
.unimplemented(),
|
.attr(TeraBlastTypeAttr)
|
||||||
|
.attr(TeraBlastPowerAttr)
|
||||||
|
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR))
|
||||||
|
.partial(),
|
||||||
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
|
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
|
||||||
.attr(ProtectAttr, BattlerTagType.SILK_TRAP),
|
.attr(ProtectAttr, BattlerTagType.SILK_TRAP),
|
||||||
new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9)
|
new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9)
|
||||||
|
@ -584,6 +584,10 @@ export class Arena {
|
|||||||
return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
|
return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasTag(tagType: ArenaTagType) : boolean {
|
||||||
|
return !!this.getTag(tagType);
|
||||||
|
}
|
||||||
|
|
||||||
getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined {
|
getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined {
|
||||||
return typeof(tagType) === "string"
|
return typeof(tagType) === "string"
|
||||||
? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side))
|
? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side))
|
||||||
|
@ -978,12 +978,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// this.scene potentially can be undefined for a fainted pokemon in doubles
|
// this.scene potentially can be undefined for a fainted pokemon in doubles
|
||||||
// use optional chaining to avoid runtime errors
|
// use optional chaining to avoid runtime errors
|
||||||
if (forDefend && (this.getTag(GroundedTag) || this.scene?.arena.getTag(ArenaTagType.GRAVITY))) {
|
|
||||||
const flyingIndex = types.indexOf(Type.FLYING);
|
|
||||||
if (flyingIndex > -1) {
|
|
||||||
types.splice(flyingIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!types.length) { // become UNKNOWN if no types are present
|
if (!types.length) { // become UNKNOWN if no types are present
|
||||||
types.push(Type.UNKNOWN);
|
types.push(Type.UNKNOWN);
|
||||||
@ -1272,6 +1266,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.isTerastallized() ? 2 : 1;
|
return this.isTerastallized() ? 2 : 1;
|
||||||
}
|
}
|
||||||
const types = this.getTypes(true, true);
|
const types = this.getTypes(true, true);
|
||||||
|
const arena = this.scene.arena;
|
||||||
|
|
||||||
|
// Handle flying v ground type immunity without removing flying type so effective types are still effective
|
||||||
|
// Related to https://github.com/pagefaultgames/pokerogue/issues/524
|
||||||
|
if (moveType === Type.GROUND && (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY))) {
|
||||||
|
const flyingIndex = types.indexOf(Type.FLYING);
|
||||||
|
if (flyingIndex > -1) {
|
||||||
|
types.splice(flyingIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let multiplier = types.map(defType => {
|
let multiplier = types.map(defType => {
|
||||||
if (source) {
|
if (source) {
|
||||||
@ -1293,7 +1297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
||||||
|
|
||||||
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
||||||
if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
||||||
multiplier /= 2;
|
multiplier /= 2;
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||||
|
@ -69,5 +69,5 @@ export const battlerTags: SimpleTranslationEntries = {
|
|||||||
"saltCuredLapse": "{{pokemonNameWithAffix}} wurde durch {{moveName}} verletzt!",
|
"saltCuredLapse": "{{pokemonNameWithAffix}} wurde durch {{moveName}} verletzt!",
|
||||||
"cursedOnAdd": "{{pokemonNameWithAffix}} nimmt einen Teil seiner KP und legt einen Fluch auf {{pokemonName}}!",
|
"cursedOnAdd": "{{pokemonNameWithAffix}} nimmt einen Teil seiner KP und legt einen Fluch auf {{pokemonName}}!",
|
||||||
"cursedLapse": "{{pokemonNameWithAffix}} wurde durch den Fluch verletzt!",
|
"cursedLapse": "{{pokemonNameWithAffix}} wurde durch den Fluch verletzt!",
|
||||||
"stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!",
|
"stockpilingOnAdd": "{{pokemonNameWithAffix}} hortet {{stockpiledCount}}!",
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
|
|||||||
*/
|
*/
|
||||||
export const starterSelectUiHandler: SimpleTranslationEntries = {
|
export const starterSelectUiHandler: SimpleTranslationEntries = {
|
||||||
"confirmStartTeam": "Mit diesen Pokémon losziehen?",
|
"confirmStartTeam": "Mit diesen Pokémon losziehen?",
|
||||||
"confirmExit": "Do you want to exit?",
|
"confirmExit": "Willst du zurück?",
|
||||||
"invalidParty": "Das ist kein gültiges Team!",
|
"invalidParty": "Das ist kein gültiges Team!",
|
||||||
"gen1": "I",
|
"gen1": "I",
|
||||||
"gen2": "II",
|
"gen2": "II",
|
||||||
@ -28,8 +28,8 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
|
|||||||
"toggleIVs": "DVs anzeigen/verbergen",
|
"toggleIVs": "DVs anzeigen/verbergen",
|
||||||
"manageMoves": "Attacken ändern",
|
"manageMoves": "Attacken ändern",
|
||||||
"manageNature": "Wesen ändern",
|
"manageNature": "Wesen ändern",
|
||||||
"addToFavorites": "Add to Favorites",
|
"addToFavorites": "Zu Favoriten hinzufügen",
|
||||||
"removeFromFavorites": "Remove from Favorites",
|
"removeFromFavorites": "Von Favoriten entfernen",
|
||||||
"useCandies": "Bonbons verwenden",
|
"useCandies": "Bonbons verwenden",
|
||||||
"selectNature": "Wähle das neue Wesen.",
|
"selectNature": "Wähle das neue Wesen.",
|
||||||
"selectMoveSwapOut": "Wähle die zu ersetzende Attacke.",
|
"selectMoveSwapOut": "Wähle die zu ersetzende Attacke.",
|
||||||
|
@ -8,7 +8,7 @@ export const moveTriggers: SimpleTranslationEntries = {
|
|||||||
"goingAllOutForAttack": "{{pokemonName}}[[는]]\n전력을 다하기 시작했다!",
|
"goingAllOutForAttack": "{{pokemonName}}[[는]]\n전력을 다하기 시작했다!",
|
||||||
"regainedHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!",
|
"regainedHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!",
|
||||||
"keptGoingAndCrashed": "{{pokemonName}}[[는]]\n의욕이 넘쳐서 땅에 부딪쳤다!",
|
"keptGoingAndCrashed": "{{pokemonName}}[[는]]\n의욕이 넘쳐서 땅에 부딪쳤다!",
|
||||||
"fled": "{{pokemonName}}[[는]]\N도망쳤다!",
|
"fled": "{{pokemonName}}[[는]]\n도망쳤다!",
|
||||||
"cannotBeSwitchedOut": "{{pokemonName}}[[를]]\n돌아오게 할 수 없습니다!",
|
"cannotBeSwitchedOut": "{{pokemonName}}[[를]]\n돌아오게 할 수 없습니다!",
|
||||||
"swappedAbilitiesWithTarget": "{{pokemonName}}[[는]]\n서로의 특성을 교체했다!",
|
"swappedAbilitiesWithTarget": "{{pokemonName}}[[는]]\n서로의 특성을 교체했다!",
|
||||||
"coinsScatteredEverywhere": "돈이 주위에 흩어졌다!",
|
"coinsScatteredEverywhere": "돈이 주위에 흩어졌다!",
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { allMoves } from "#app/data/move.js";
|
import { allMoves } from "#app/data/move";
|
||||||
import { Abilities } from "#app/enums/abilities.js";
|
import { Abilities } from "#app/enums/abilities";
|
||||||
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase.js";
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
|
||||||
|
|
||||||
describe("Arena - Gravity", () => {
|
describe("Arena - Gravity", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -26,14 +27,17 @@ describe("Arena - Gravity", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleType("single");
|
game.override
|
||||||
game.override.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]);
|
.battleType("single")
|
||||||
game.override.ability(Abilities.UNNERVE);
|
.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE])
|
||||||
game.override.enemyAbility(Abilities.BALL_FETCH);
|
.ability(Abilities.UNNERVE)
|
||||||
game.override.enemySpecies(Species.SHUCKLE);
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH));
|
.enemySpecies(Species.SHUCKLE)
|
||||||
|
.enemyMoveset(SPLASH_ONLY);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reference: https://bulbapedia.bulbagarden.net/wiki/Gravity_(move)
|
||||||
|
|
||||||
it("non-OHKO move accuracy is multiplied by 1.67", async () => {
|
it("non-OHKO move accuracy is multiplied by 1.67", async () => {
|
||||||
const moveToCheck = allMoves[Moves.TACKLE];
|
const moveToCheck = allMoves[Moves.TACKLE];
|
||||||
|
|
||||||
@ -77,4 +81,65 @@ describe("Arena - Gravity", () => {
|
|||||||
|
|
||||||
expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30);
|
expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Against flying types", () => {
|
||||||
|
it("can be hit by ground-type moves now", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(5)
|
||||||
|
.enemyLevel(5)
|
||||||
|
.enemySpecies(Species.PIDGEOT)
|
||||||
|
.moveset([Moves.GRAVITY, Moves.EARTHQUAKE]);
|
||||||
|
|
||||||
|
await game.startBattle([Species.PIKACHU]);
|
||||||
|
|
||||||
|
const pidgeot = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
|
||||||
|
|
||||||
|
// Try earthquake on 1st turn (fails!);
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(0);
|
||||||
|
|
||||||
|
// Setup Gravity on 2nd turn
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined();
|
||||||
|
|
||||||
|
// Use ground move on 3rd turn
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps super-effective moves super-effective after using gravity", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(5)
|
||||||
|
.enemyLevel(5)
|
||||||
|
.enemySpecies(Species.PIDGEOT)
|
||||||
|
.moveset([Moves.GRAVITY, Moves.THUNDERBOLT]);
|
||||||
|
|
||||||
|
await game.startBattle([Species.PIKACHU]);
|
||||||
|
|
||||||
|
const pidgeot = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
|
||||||
|
|
||||||
|
// Setup Gravity on 1st turn
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined();
|
||||||
|
|
||||||
|
// Use electric move on 2nd turn
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
106
src/test/moves/tera_blast.test.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { getMovePosition } from "../utils/gameManagerUtils";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { HitResult } from "#app/field/pokemon";
|
||||||
|
|
||||||
|
describe("Moves - Tera Blast", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const moveToCheck = allMoves[Moves.TERA_BLAST];
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.moveset([Moves.TERA_BLAST])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.startingHeldItems([{name: "TERA_SHARD", type: Type.FIRE}])
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyLevel(20);
|
||||||
|
|
||||||
|
vi.spyOn(moveToCheck, "calculateBattlePower");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes type to match user's tera type", async() => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.FURRET)
|
||||||
|
.startingHeldItems([{name: "TERA_SHARD", type: Type.FIGHTING}]);
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(enemyPokemon, "apply");
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST));
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
|
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("increases power if user is Stellar tera type", async() => {
|
||||||
|
game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]);
|
||||||
|
const stellarTypeMultiplier = 2;
|
||||||
|
const stellarTypeDmgBonus = 20;
|
||||||
|
const basePower = moveToCheck.power;
|
||||||
|
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST));
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
|
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith((basePower + stellarTypeDmgBonus) * stellarTypeMultiplier);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
// Currently abilities are bugged and can't see when a move's category is changed
|
||||||
|
it.skip("uses the higher stat of the user's Atk and SpAtk for damage calculation", async() => {
|
||||||
|
game.override.enemyAbility(Abilities.TOXIC_DEBRIS);
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
playerPokemon.stats[Stat.ATK] = 100;
|
||||||
|
playerPokemon.stats[Stat.SPATK] = 1;
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST));
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("causes stat drops if user is Stellar tera type", async() => {
|
||||||
|
game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]);
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST));
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
|
expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1);
|
||||||
|
expect(playerPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1);
|
||||||
|
}, 20000);
|
||||||
|
});
|