mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-19 22:09:27 +02:00
Merge branch 'beta' into bugfix-egg-hatch-crash-missing-sprite
This commit is contained in:
commit
52a22932bb
@ -5713,9 +5713,7 @@ export function initAbilities() {
|
|||||||
.condition(getSheerForceHitDisableAbCondition()),
|
.condition(getSheerForceHitDisableAbCondition()),
|
||||||
new Ability(Abilities.SHEER_FORCE, 5)
|
new Ability(Abilities.SHEER_FORCE, 5)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096)
|
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096)
|
||||||
.attr(MoveEffectChanceMultiplierAbAttr, 0)
|
.attr(MoveEffectChanceMultiplierAbAttr, 0), // Should disable life orb, eject button, red card, kee/maranga berry if they get implemented
|
||||||
.edgeCase() // Should disable shell bell and Meloetta's relic song transformation
|
|
||||||
.edgeCase(), // Should disable life orb, eject button, red card, kee/maranga berry if they get implemented
|
|
||||||
new Ability(Abilities.CONTRARY, 5)
|
new Ability(Abilities.CONTRARY, 5)
|
||||||
.attr(StatStageChangeMultiplierAbAttr, -1)
|
.attr(StatStageChangeMultiplierAbAttr, -1)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
|
@ -16,9 +16,9 @@ interface PokemonSpeciesFormLevelMoves {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Moves that can only be learned with a memory-mushroom */
|
/** Moves that can only be learned with a memory-mushroom */
|
||||||
const RELEARN_MOVE = -1;
|
export const RELEARN_MOVE = -1;
|
||||||
/** Moves that can only be learned with an evolve */
|
/** Moves that can only be learned with an evolve */
|
||||||
const EVOLVE_MOVE = 0;
|
export const EVOLVE_MOVE = 0;
|
||||||
|
|
||||||
export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
|
export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
|
||||||
[Species.BULBASAUR]: [
|
[Species.BULBASAUR]: [
|
||||||
|
@ -351,6 +351,10 @@ export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMove
|
|||||||
if (pokemon.scene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
if (pokemon.scene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song
|
||||||
|
if (pokemon.hasAbility(Abilities.SHEER_FORCE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return super.canChange(pokemon);
|
return super.canChange(pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import { BattlerIndex } from "#app/battle";
|
|||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { LevelMoves } from "#app/data/balance/pokemon-level-moves";
|
import { EVOLVE_MOVE, LevelMoves, RELEARN_MOVE } from "#app/data/balance/pokemon-level-moves";
|
||||||
import { DamageAchv, achvs } from "#app/system/achv";
|
import { DamageAchv, achvs } from "#app/system/achv";
|
||||||
import { DexAttr, StarterDataEntry, StarterMoveset } from "#app/system/game-data";
|
import { DexAttr, StarterDataEntry, StarterMoveset } from "#app/system/game-data";
|
||||||
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
|
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
|
||||||
@ -71,6 +71,15 @@ import { Nature } from "#enums/nature";
|
|||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { doShinySparkleAnim } from "#app/field/anims";
|
import { doShinySparkleAnim } from "#app/field/anims";
|
||||||
|
|
||||||
|
export enum LearnMoveSituation {
|
||||||
|
MISC,
|
||||||
|
LEVEL_UP,
|
||||||
|
RELEARN,
|
||||||
|
EVOLUTION,
|
||||||
|
EVOLUTION_FUSED, // If fusionSpecies has Evolved
|
||||||
|
EVOLUTION_FUSED_BASE // If fusion's base species has Evolved
|
||||||
|
}
|
||||||
|
|
||||||
export enum FieldPosition {
|
export enum FieldPosition {
|
||||||
CENTER,
|
CENTER,
|
||||||
LEFT,
|
LEFT,
|
||||||
@ -1817,40 +1826,44 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param {boolean} includeRelearnerMoves Whether to include moves that would require a relearner. Note the move relearner inherently allows evolution moves
|
* @param {boolean} includeRelearnerMoves Whether to include moves that would require a relearner. Note the move relearner inherently allows evolution moves
|
||||||
* @returns {LevelMoves} A list of moves and the levels they can be learned at
|
* @returns {LevelMoves} A list of moves and the levels they can be learned at
|
||||||
*/
|
*/
|
||||||
getLevelMoves(startingLevel?: integer, includeEvolutionMoves: boolean = false, simulateEvolutionChain: boolean = false, includeRelearnerMoves: boolean = false): LevelMoves {
|
getLevelMoves(startingLevel?: integer, includeEvolutionMoves: boolean = false, simulateEvolutionChain: boolean = false, includeRelearnerMoves: boolean = false, learnSituation: LearnMoveSituation = LearnMoveSituation.MISC): LevelMoves {
|
||||||
const ret: LevelMoves = [];
|
const ret: LevelMoves = [];
|
||||||
let levelMoves: LevelMoves = [];
|
let levelMoves: LevelMoves = [];
|
||||||
if (!startingLevel) {
|
if (!startingLevel) {
|
||||||
startingLevel = this.level;
|
startingLevel = this.level;
|
||||||
}
|
}
|
||||||
if (simulateEvolutionChain) {
|
if (learnSituation === LearnMoveSituation.EVOLUTION_FUSED && this.fusionSpecies) { // For fusion evolutions, get ONLY the moves of the component mon that evolved
|
||||||
const evolutionChain = this.species.getSimulatedEvolutionChain(this.level, this.hasTrainer(), this.isBoss(), this.isPlayer());
|
levelMoves = this.getFusionSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || (includeRelearnerMoves && lm[0] === RELEARN_MOVE) || lm[0] > 0);
|
||||||
for (let e = 0; e < evolutionChain.length; e++) {
|
|
||||||
// TODO: Might need to pass specific form index in simulated evolution chain
|
|
||||||
const speciesLevelMoves = getPokemonSpeciesForm(evolutionChain[e][0], this.formIndex).getLevelMoves();
|
|
||||||
if (includeRelearnerMoves) {
|
|
||||||
levelMoves.push(...speciesLevelMoves);
|
|
||||||
} else {
|
|
||||||
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1]))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
levelMoves = this.getSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === 0) || (includeRelearnerMoves && lm[0] === -1) || lm[0] > 0);
|
|
||||||
}
|
|
||||||
if (this.fusionSpecies) {
|
|
||||||
if (simulateEvolutionChain) {
|
if (simulateEvolutionChain) {
|
||||||
const fusionEvolutionChain = this.fusionSpecies.getSimulatedEvolutionChain(this.level, this.hasTrainer(), this.isBoss(), this.isPlayer());
|
const evolutionChain = this.species.getSimulatedEvolutionChain(this.level, this.hasTrainer(), this.isBoss(), this.isPlayer());
|
||||||
for (let e = 0; e < fusionEvolutionChain.length; e++) {
|
for (let e = 0; e < evolutionChain.length; e++) {
|
||||||
// TODO: Might need to pass specific form index in simulated evolution chain
|
// TODO: Might need to pass specific form index in simulated evolution chain
|
||||||
const speciesLevelMoves = getPokemonSpeciesForm(fusionEvolutionChain[e][0], this.fusionFormIndex).getLevelMoves();
|
const speciesLevelMoves = getPokemonSpeciesForm(evolutionChain[e][0], this.formIndex).getLevelMoves();
|
||||||
if (includeRelearnerMoves) {
|
if (includeRelearnerMoves) {
|
||||||
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || lm[0] !== 0));
|
levelMoves.push(...speciesLevelMoves);
|
||||||
} else {
|
} else {
|
||||||
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || ((!e || lm[0] > 1) && (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1]))));
|
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1]))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
levelMoves.push(...this.getFusionSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === 0) || (includeRelearnerMoves && lm[0] === -1) || lm[0] > 0));
|
levelMoves = this.getSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || (includeRelearnerMoves && lm[0] === RELEARN_MOVE) || lm[0] > 0);
|
||||||
|
}
|
||||||
|
if (this.fusionSpecies && learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE) { // For fusion evolutions, get ONLY the moves of the component mon that evolved
|
||||||
|
if (simulateEvolutionChain) {
|
||||||
|
const fusionEvolutionChain = this.fusionSpecies.getSimulatedEvolutionChain(this.level, this.hasTrainer(), this.isBoss(), this.isPlayer());
|
||||||
|
for (let e = 0; e < fusionEvolutionChain.length; e++) {
|
||||||
|
// TODO: Might need to pass specific form index in simulated evolution chain
|
||||||
|
const speciesLevelMoves = getPokemonSpeciesForm(fusionEvolutionChain[e][0], this.fusionFormIndex).getLevelMoves();
|
||||||
|
if (includeRelearnerMoves) {
|
||||||
|
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || lm[0] !== EVOLVE_MOVE));
|
||||||
|
} else {
|
||||||
|
levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || ((!e || lm[0] > 1) && (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1]))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
levelMoves.push(...this.getFusionSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || (includeRelearnerMoves && lm[0] === RELEARN_MOVE) || lm[0] > 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
levelMoves.sort((lma: [integer, integer], lmb: [integer, integer]) => lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0);
|
levelMoves.sort((lma: [integer, integer], lmb: [integer, integer]) => lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0);
|
||||||
@ -4279,28 +4292,29 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addFriendship(friendship: integer): void {
|
addFriendship(friendship: number): void {
|
||||||
const starterSpeciesId = this.species.getRootSpeciesId();
|
if (friendship > 0) {
|
||||||
const fusionStarterSpeciesId = this.isFusion() && this.fusionSpecies ? this.fusionSpecies.getRootSpeciesId() : 0;
|
const starterSpeciesId = this.species.getRootSpeciesId();
|
||||||
const starterData = [
|
const fusionStarterSpeciesId = this.isFusion() && this.fusionSpecies ? this.fusionSpecies.getRootSpeciesId() : 0;
|
||||||
this.scene.gameData.starterData[starterSpeciesId],
|
const starterData = [
|
||||||
fusionStarterSpeciesId ? this.scene.gameData.starterData[fusionStarterSpeciesId] : null
|
this.scene.gameData.starterData[starterSpeciesId],
|
||||||
].filter(d => !!d);
|
fusionStarterSpeciesId ? this.scene.gameData.starterData[fusionStarterSpeciesId] : null
|
||||||
const amount = new Utils.IntegerHolder(friendship);
|
].filter(d => !!d);
|
||||||
let candyFriendshipMultiplier = CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER;
|
const amount = new Utils.NumberHolder(friendship);
|
||||||
if (this.scene.eventManager.isEventActive()) {
|
|
||||||
candyFriendshipMultiplier *= this.scene.eventManager.getFriendshipMultiplier();
|
|
||||||
}
|
|
||||||
const starterAmount = new Utils.IntegerHolder(Math.floor(friendship * (this.scene.gameMode.isClassic && friendship > 0 ? candyFriendshipMultiplier : 1) / (fusionStarterSpeciesId ? 2 : 1)));
|
|
||||||
if (amount.value > 0) {
|
|
||||||
this.scene.applyModifier(PokemonFriendshipBoosterModifier, true, this, amount);
|
this.scene.applyModifier(PokemonFriendshipBoosterModifier, true, this, amount);
|
||||||
this.scene.applyModifier(PokemonFriendshipBoosterModifier, true, this, starterAmount);
|
let candyFriendshipMultiplier = CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER;
|
||||||
|
if (this.scene.eventManager.isEventActive()) {
|
||||||
|
candyFriendshipMultiplier *= this.scene.eventManager.getFriendshipMultiplier();
|
||||||
|
}
|
||||||
|
const starterAmount = new Utils.NumberHolder(Math.floor(amount.value * (this.scene.gameMode.isClassic ? candyFriendshipMultiplier : 1) / (fusionStarterSpeciesId ? 2 : 1)));
|
||||||
|
|
||||||
|
// Add friendship to this PlayerPokemon
|
||||||
this.friendship = Math.min(this.friendship + amount.value, 255);
|
this.friendship = Math.min(this.friendship + amount.value, 255);
|
||||||
if (this.friendship === 255) {
|
if (this.friendship === 255) {
|
||||||
this.scene.validateAchv(achvs.MAX_FRIENDSHIP);
|
this.scene.validateAchv(achvs.MAX_FRIENDSHIP);
|
||||||
}
|
}
|
||||||
starterData.forEach((sd: StarterDataEntry, i: integer) => {
|
// Add to candy progress for this mon's starter species and its fused species (if it has one)
|
||||||
|
starterData.forEach((sd: StarterDataEntry, i: number) => {
|
||||||
const speciesId = !i ? starterSpeciesId : fusionStarterSpeciesId as Species;
|
const speciesId = !i ? starterSpeciesId : fusionStarterSpeciesId as Species;
|
||||||
sd.friendship = (sd.friendship || 0) + starterAmount.value;
|
sd.friendship = (sd.friendship || 0) + starterAmount.value;
|
||||||
if (sd.friendship >= getStarterValueFriendshipCap(speciesStarterCosts[speciesId])) {
|
if (sd.friendship >= getStarterValueFriendshipCap(speciesStarterCosts[speciesId])) {
|
||||||
@ -4309,10 +4323,8 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.friendship = Math.max(this.friendship + amount.value, 0);
|
// Lose friendship upon fainting
|
||||||
for (const sd of starterData) {
|
this.friendship = Math.max(this.friendship + friendship, 0);
|
||||||
sd.friendship = Math.max((sd.friendship || 0) + starterAmount.value, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,6 @@ import type { VoucherType } from "#app/system/voucher";
|
|||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
import { addTextObject, TextStyle } from "#app/ui/text";
|
import { addTextObject, TextStyle } from "#app/ui/text";
|
||||||
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils";
|
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
@ -726,22 +725,6 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Applies to items with chance of activating secondary effects ie Kings Rock
|
|
||||||
getSecondaryChanceMultiplier(pokemon: Pokemon): number {
|
|
||||||
// Temporary quickfix to stop game from freezing when the opponet uses u-turn while holding on to king's rock
|
|
||||||
if (!pokemon.getLastXMoves()[0]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const sheerForceAffected = allMoves[pokemon.getLastXMoves()[0].move].chance >= 0 && pokemon.hasAbility(Abilities.SHEER_FORCE);
|
|
||||||
|
|
||||||
if (sheerForceAffected) {
|
|
||||||
return 0;
|
|
||||||
} else if (pokemon.hasAbility(Abilities.SERENE_GRACE)) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
|
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
|
||||||
const pokemon = this.getPokemon(scene);
|
const pokemon = this.getPokemon(scene);
|
||||||
if (!pokemon) {
|
if (!pokemon) {
|
||||||
@ -1614,9 +1597,16 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for Pokemon held items like King's Rock
|
||||||
|
* Because King's Rock can be stacked in PokeRogue, unlike mainline, it does not receive a boost from Abilities.SERENE_GRACE
|
||||||
|
*/
|
||||||
export class FlinchChanceModifier extends PokemonHeldItemModifier {
|
export class FlinchChanceModifier extends PokemonHeldItemModifier {
|
||||||
|
private chance: number;
|
||||||
constructor(type: ModifierType, pokemonId: number, stackCount?: number) {
|
constructor(type: ModifierType, pokemonId: number, stackCount?: number) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
|
||||||
|
this.chance = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchType(modifier: Modifier) {
|
matchType(modifier: Modifier) {
|
||||||
@ -1644,7 +1634,8 @@ export class FlinchChanceModifier extends PokemonHeldItemModifier {
|
|||||||
* @returns `true` if {@linkcode FlinchChanceModifier} has been applied
|
* @returns `true` if {@linkcode FlinchChanceModifier} has been applied
|
||||||
*/
|
*/
|
||||||
override apply(pokemon: Pokemon, flinched: BooleanHolder): boolean {
|
override apply(pokemon: Pokemon, flinched: BooleanHolder): boolean {
|
||||||
if (!flinched.value && pokemon.randSeedInt(10) < (this.getStackCount() * this.getSecondaryChanceMultiplier(pokemon))) {
|
// The check for pokemon.battleSummonData is to ensure that a crash doesn't occur when a Pokemon with King's Rock procs a flinch
|
||||||
|
if (pokemon.battleSummonData && !flinched.value && pokemon.randSeedInt(100) < (this.getStackCount() * this.chance)) {
|
||||||
flinched.value = true;
|
flinched.value = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1652,7 +1643,7 @@ export class FlinchChanceModifier extends PokemonHeldItemModifier {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMaxHeldItemCount(pokemon: Pokemon): number {
|
getMaxHeldItemCount(_pokemon: Pokemon): number {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import BattleScene, { AnySound } from "#app/battle-scene";
|
import BattleScene, { AnySound } from "#app/battle-scene";
|
||||||
import { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
import { FusionSpeciesFormEvolution, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
||||||
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
|
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { cos, sin } from "#app/field/anims";
|
import { cos, sin } from "#app/field/anims";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { LearnMoveSituation, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
||||||
|
import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves";
|
||||||
|
|
||||||
export class EvolutionPhase extends Phase {
|
export class EvolutionPhase extends Phase {
|
||||||
protected pokemon: PlayerPokemon;
|
protected pokemon: PlayerPokemon;
|
||||||
@ -20,6 +21,7 @@ export class EvolutionPhase extends Phase {
|
|||||||
private preEvolvedPokemonName: string;
|
private preEvolvedPokemonName: string;
|
||||||
|
|
||||||
private evolution: SpeciesFormEvolution | null;
|
private evolution: SpeciesFormEvolution | null;
|
||||||
|
private fusionSpeciesEvolved: boolean; // Whether the evolution is of the fused species
|
||||||
private evolutionBgm: AnySound;
|
private evolutionBgm: AnySound;
|
||||||
private evolutionHandler: EvolutionSceneHandler;
|
private evolutionHandler: EvolutionSceneHandler;
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ export class EvolutionPhase extends Phase {
|
|||||||
this.pokemon = pokemon;
|
this.pokemon = pokemon;
|
||||||
this.evolution = evolution;
|
this.evolution = evolution;
|
||||||
this.lastLevel = lastLevel;
|
this.lastLevel = lastLevel;
|
||||||
|
this.fusionSpeciesEvolved = evolution instanceof FusionSpeciesFormEvolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): boolean {
|
validate(): boolean {
|
||||||
@ -273,7 +276,8 @@ export class EvolutionPhase extends Phase {
|
|||||||
this.evolutionHandler.canCancel = false;
|
this.evolutionHandler.canCancel = false;
|
||||||
|
|
||||||
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
||||||
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
|
const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved ? LearnMoveSituation.EVOLUTION_FUSED : this.pokemon.fusionSpecies ? LearnMoveSituation.EVOLUTION_FUSED_BASE : LearnMoveSituation.EVOLUTION;
|
||||||
|
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation).filter(lm => lm[0] === EVOLVE_MOVE);
|
||||||
for (const lm of levelMoves) {
|
for (const lm of levelMoves) {
|
||||||
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getPlayerParty().indexOf(this.pokemon), lm[1]));
|
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getPlayerParty().indexOf(this.pokemon), lm[1]));
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability";
|
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
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 GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { FlinchAttr } from "#app/data/move";
|
||||||
|
|
||||||
describe("Abilities - Serene Grace", () => {
|
describe("Abilities - Serene Grace", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -27,66 +24,26 @@ describe("Abilities - Serene Grace", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
const movesToUse = [ Moves.AIR_SLASH, Moves.TACKLE ];
|
game.override
|
||||||
game.override.battleType("single");
|
.battleType("single")
|
||||||
game.override.enemySpecies(Species.ONIX);
|
.ability(Abilities.SERENE_GRACE)
|
||||||
game.override.startingLevel(100);
|
.moveset([ Moves.AIR_SLASH, Moves.TACKLE ])
|
||||||
game.override.moveset(movesToUse);
|
.enemyLevel(10)
|
||||||
game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]);
|
.enemyMoveset([ Moves.SPLASH ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Move chance without Serene Grace", async () => {
|
it("Serene Grace should double the secondary effect chance of a move", async () => {
|
||||||
const moveToUse = Moves.AIR_SLASH;
|
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||||
await game.startBattle([
|
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
const airSlashMove = allMoves[Moves.AIR_SLASH];
|
||||||
|
const airSlashFlinchAttr = airSlashMove.getAttrs(FlinchAttr)[0];
|
||||||
|
vi.spyOn(airSlashFlinchAttr, "getMoveChance");
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
game.move.select(Moves.AIR_SLASH);
|
||||||
expect(game.scene.getPlayerParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
// Check chance of Air Slash without Serene Grace
|
expect(airSlashFlinchAttr.getMoveChance).toHaveLastReturnedWith(60);
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
});
|
||||||
const move = phase.move.getMove();
|
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
|
||||||
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
|
||||||
expect(chance.value).toBe(30);
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("Move chance with Serene Grace", async () => {
|
|
||||||
const moveToUse = Moves.AIR_SLASH;
|
|
||||||
game.override.ability(Abilities.SERENE_GRACE);
|
|
||||||
await game.startBattle([
|
|
||||||
Species.TOGEKISS
|
|
||||||
]);
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
|
||||||
expect(game.scene.getPlayerParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
|
||||||
|
|
||||||
// Check chance of Air Slash with Serene Grace
|
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
|
||||||
const move = phase.move.getMove();
|
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
|
||||||
expect(chance.value).toBe(60);
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
//TODO King's Rock Interaction Unit Test
|
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability";
|
import { Type } from "#app/enums/type";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
|
||||||
import { NumberHolder } from "#app/utils";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves, FlinchAttr } from "#app/data/move";
|
||||||
|
|
||||||
describe("Abilities - Sheer Force", () => {
|
describe("Abilities - Sheer Force", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -27,143 +25,91 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
const movesToUse = [ Moves.AIR_SLASH, Moves.BIND, Moves.CRUSH_CLAW, Moves.TACKLE ];
|
game.override
|
||||||
game.override.battleType("single");
|
.battleType("single")
|
||||||
game.override.enemySpecies(Species.ONIX);
|
.ability(Abilities.SHEER_FORCE)
|
||||||
game.override.startingLevel(100);
|
.enemySpecies(Species.ONIX)
|
||||||
game.override.moveset(movesToUse);
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]);
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.disableCrits();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Sheer Force", async () => {
|
const SHEER_FORCE_MULT = 5461 / 4096;
|
||||||
const moveToUse = Moves.AIR_SLASH;
|
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
it("Sheer Force should boost the power of the move but disable secondary effects", async () => {
|
||||||
|
game.override.moveset([ Moves.AIR_SLASH ]);
|
||||||
|
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const airSlashMove = allMoves[Moves.AIR_SLASH];
|
||||||
|
vi.spyOn(airSlashMove, "calculateBattlePower");
|
||||||
|
const airSlashFlinchAttr = airSlashMove.getAttrs(FlinchAttr)[0];
|
||||||
|
vi.spyOn(airSlashFlinchAttr, "getMoveChance");
|
||||||
|
|
||||||
|
game.move.select(Moves.AIR_SLASH);
|
||||||
|
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(airSlashMove.calculateBattlePower).toHaveLastReturnedWith(airSlashMove.power * SHEER_FORCE_MULT);
|
||||||
|
expect(airSlashFlinchAttr.getMoveChance).toHaveLastReturnedWith(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Sheer Force does not affect the base damage or secondary effects of binding moves", async () => {
|
||||||
|
game.override.moveset([ Moves.BIND ]);
|
||||||
|
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const bindMove = allMoves[Moves.BIND];
|
||||||
|
vi.spyOn(bindMove, "calculateBattlePower");
|
||||||
|
|
||||||
|
game.move.select(Moves.BIND);
|
||||||
|
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(bindMove.calculateBattlePower).toHaveLastReturnedWith(bindMove.power);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("Sheer Force does not boost the base damage of moves with no secondary effect", async () => {
|
||||||
|
game.override.moveset([ Moves.TACKLE ]);
|
||||||
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
|
|
||||||
game.scene.getEnemyPokemon()!.stats[Stat.SPDEF] = 10000;
|
const tackleMove = allMoves[Moves.TACKLE];
|
||||||
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
vi.spyOn(tackleMove, "calculateBattlePower");
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
|
game.move.select(Moves.TACKLE);
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
expect(tackleMove.calculateBattlePower).toHaveLastReturnedWith(tackleMove.power);
|
||||||
const move = phase.move.getMove();
|
});
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
|
||||||
|
|
||||||
//Verify the move is boosted and has no chance of secondary effects
|
it("Sheer Force can disable the on-hit activation of specific abilities", async () => {
|
||||||
const power = new NumberHolder(move.power);
|
game.override
|
||||||
const chance = new NumberHolder(move.chance);
|
.moveset([ Moves.HEADBUTT ])
|
||||||
|
.enemySpecies(Species.SQUIRTLE)
|
||||||
|
.enemyLevel(10)
|
||||||
|
.enemyAbility(Abilities.COLOR_CHANGE);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
|
||||||
|
|
||||||
expect(chance.value).toBe(0);
|
|
||||||
expect(power.value).toBe(move.power * 5461 / 4096);
|
|
||||||
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("Sheer Force with exceptions including binding moves", async () => {
|
|
||||||
const moveToUse = Moves.BIND;
|
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
|
||||||
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||||
|
const headbuttMove = allMoves[Moves.HEADBUTT];
|
||||||
|
vi.spyOn(headbuttMove, "calculateBattlePower");
|
||||||
|
const headbuttFlinchAttr = headbuttMove.getAttrs(FlinchAttr)[0];
|
||||||
|
vi.spyOn(headbuttFlinchAttr, "getMoveChance");
|
||||||
|
|
||||||
|
game.move.select(Moves.HEADBUTT);
|
||||||
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
|
||||||
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
expect(enemyPokemon?.getTypes()[0]).toBe(Type.WATER);
|
||||||
const move = phase.move.getMove();
|
expect(headbuttMove.calculateBattlePower).toHaveLastReturnedWith(headbuttMove.power * SHEER_FORCE_MULT);
|
||||||
expect(move.id).toBe(Moves.BIND);
|
expect(headbuttFlinchAttr.getMoveChance).toHaveLastReturnedWith(0);
|
||||||
|
});
|
||||||
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
|
||||||
const power = new NumberHolder(move.power);
|
|
||||||
const chance = new NumberHolder(move.chance);
|
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
|
||||||
expect(power.value).toBe(move.power);
|
|
||||||
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("Sheer Force with moves with no secondary effect", async () => {
|
|
||||||
const moveToUse = Moves.TACKLE;
|
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
|
||||||
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
|
||||||
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
|
||||||
|
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
|
||||||
const move = phase.move.getMove();
|
|
||||||
expect(move.id).toBe(Moves.TACKLE);
|
|
||||||
|
|
||||||
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
|
||||||
const power = new NumberHolder(move.power);
|
|
||||||
const chance = new NumberHolder(move.chance);
|
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
|
||||||
expect(power.value).toBe(move.power);
|
|
||||||
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("Sheer Force Disabling Specific Abilities", async () => {
|
|
||||||
const moveToUse = Moves.CRUSH_CLAW;
|
|
||||||
game.override.enemyAbility(Abilities.COLOR_CHANGE);
|
|
||||||
game.override.startingHeldItems([{ name: "KINGS_ROCK", count: 1 }]);
|
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
|
||||||
await game.startBattle([ Species.PIDGEOT ]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
|
||||||
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
|
||||||
|
|
||||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
|
||||||
const move = phase.move.getMove();
|
|
||||||
expect(move.id).toBe(Moves.CRUSH_CLAW);
|
|
||||||
|
|
||||||
//Disable color change due to being hit by Sheer Force
|
|
||||||
const power = new NumberHolder(move.power);
|
|
||||||
const chance = new NumberHolder(move.chance);
|
|
||||||
const user = phase.getUserPokemon()!;
|
|
||||||
const target = phase.getFirstTarget()!;
|
|
||||||
const opponentType = target.getTypes()[0];
|
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false);
|
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, false, power);
|
|
||||||
applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move));
|
|
||||||
|
|
||||||
expect(chance.value).toBe(0);
|
|
||||||
expect(power.value).toBe(move.power * 5461 / 4096);
|
|
||||||
expect(target.getTypes().length).toBe(2);
|
|
||||||
expect(target.getTypes()[0]).toBe(opponentType);
|
|
||||||
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("Two Pokemon with abilities disabled by Sheer Force hitting each other should not cause a crash", async () => {
|
it("Two Pokemon with abilities disabled by Sheer Force hitting each other should not cause a crash", async () => {
|
||||||
const moveToUse = Moves.CRUNCH;
|
const moveToUse = Moves.CRUNCH;
|
||||||
@ -191,5 +137,19 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
expect(onix.getTypes()).toStrictEqual(expectedTypes);
|
expect(onix.getTypes()).toStrictEqual(expectedTypes);
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO King's Rock Interaction Unit Test
|
it("Sheer Force should disable Meloetta's transformation from Relic Song", async () => {
|
||||||
|
game.override
|
||||||
|
.ability(Abilities.SHEER_FORCE)
|
||||||
|
.moveset([ Moves.RELIC_SONG ])
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.enemyLevel(100);
|
||||||
|
await game.classicMode.startBattle([ Species.MELOETTA ]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon();
|
||||||
|
const formKeyStart = playerPokemon?.getFormKey();
|
||||||
|
|
||||||
|
game.move.select(Moves.RELIC_SONG);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
expect(formKeyStart).toBe(playerPokemon?.getFormKey());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user