mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-02-24 04:57:34 +00:00
Merge branch 'beta' into pr-illusion
This commit is contained in:
commit
d56e1cf5b7
@ -2316,7 +2316,10 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
}
|
||||
|
||||
this.currentPhase?.start();
|
||||
if (this.currentPhase) {
|
||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
||||
this.currentPhase.start();
|
||||
}
|
||||
}
|
||||
|
||||
overridePhase(phase: Phase): boolean {
|
||||
@ -2326,6 +2329,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
this.standbyPhase = this.currentPhase;
|
||||
this.currentPhase = phase;
|
||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
||||
phase.start();
|
||||
|
||||
return true;
|
||||
|
@ -118,6 +118,14 @@ export class Ability implements Localizable {
|
||||
this.nameAppend += " (N)";
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case.
|
||||
* @returns the ability
|
||||
*/
|
||||
edgeCase(): this {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
|
||||
@ -5017,7 +5025,7 @@ export function initAbilities() {
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SHIELD_DUST, 3)
|
||||
.attr(IgnoreMoveEffectsAbAttr)
|
||||
.partial(),
|
||||
.edgeCase(), // Does not work with secret power (unimplemented)
|
||||
new Ability(Abilities.OWN_TEMPO, 3)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
|
||||
.attr(IntimidateImmunityAbAttr)
|
||||
@ -5062,7 +5070,7 @@ export function initAbilities() {
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SERENE_GRACE, 3)
|
||||
.attr(MoveEffectChanceMultiplierAbAttr, 2)
|
||||
.partial(),
|
||||
.edgeCase(), // does not work with secret power (unimplemented)
|
||||
new Ability(Abilities.SWIFT_SWIM, 3)
|
||||
.attr(StatMultiplierAbAttr, Stat.SPD, 2)
|
||||
.condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)),
|
||||
@ -5346,7 +5354,8 @@ export function initAbilities() {
|
||||
new Ability(Abilities.SHEER_FORCE, 5)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096)
|
||||
.attr(MoveEffectChanceMultiplierAbAttr, 0)
|
||||
.partial(),
|
||||
.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)
|
||||
.attr(StatStageChangeMultiplierAbAttr, -1)
|
||||
.ignorable(),
|
||||
@ -5389,7 +5398,7 @@ export function initAbilities() {
|
||||
/** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */
|
||||
(pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1)
|
||||
)
|
||||
.partial(),
|
||||
.edgeCase(), // Cannot recover berries used up by fling or natural gift (unimplemented)
|
||||
new Ability(Abilities.TELEPATHY, 5)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
|
||||
.ignorable(),
|
||||
@ -5476,7 +5485,7 @@ export function initAbilities() {
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.VICTORY_STAR, 5)
|
||||
.attr(StatMultiplierAbAttr, Stat.ACC, 1.1)
|
||||
.partial(),
|
||||
.partial(), // Does not boost ally's accuracy
|
||||
new Ability(Abilities.TURBOBLAZE, 5)
|
||||
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
||||
.attr(MoveAbilityBypassAbAttr),
|
||||
@ -5587,7 +5596,7 @@ export function initAbilities() {
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.bypassFaint()
|
||||
.partial(),
|
||||
.partial(), // Meteor form should protect against status effects and yawn
|
||||
new Ability(Abilities.STAKEOUT, 7)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2),
|
||||
new Ability(Abilities.WATER_BUBBLE, 7)
|
||||
@ -5655,9 +5664,9 @@ export function initAbilities() {
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.bypassFaint()
|
||||
.partial(),
|
||||
new Ability(Abilities.CORROSION, 7) // TODO: Test Corrosion against Magic Bounce once it is implemented
|
||||
new Ability(Abilities.CORROSION, 7)
|
||||
.attr(IgnoreTypeStatusEffectImmunityAbAttr, [ StatusEffect.POISON, StatusEffect.TOXIC ], [ Type.STEEL, Type.POISON ])
|
||||
.partial(),
|
||||
.edgeCase(), // Should interact correctly with magic coat/bounce (not yet implemented), fling with toxic orb (not implemented yet), and synchronize (not fully implemented yet)
|
||||
new Ability(Abilities.COMATOSE, 7)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
@ -5812,7 +5821,7 @@ export function initAbilities() {
|
||||
new Ability(Abilities.WANDERING_SPIRIT, 8)
|
||||
.attr(PostDefendAbilitySwapAbAttr)
|
||||
.bypassFaint()
|
||||
.partial(),
|
||||
.edgeCase(), // interacts incorrectly with rock head. It's meant to switch abilities before recoil would apply so that a pokemon with rock head would lose rock head first and still take the recoil
|
||||
new Ability(Abilities.GORILLA_TACTICS, 8)
|
||||
.attr(GorillaTacticsAbAttr),
|
||||
new Ability(Abilities.NEUTRALIZING_GAS, 8)
|
||||
@ -5822,7 +5831,7 @@ export function initAbilities() {
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
||||
.attr(PostSummonRemoveIllusionAbAttr)
|
||||
.partial(),
|
||||
.partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed
|
||||
new Ability(Abilities.PASTEL_VEIL, 8)
|
||||
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
@ -5927,7 +5936,7 @@ export function initAbilities() {
|
||||
new Ability(Abilities.GOOD_AS_GOLD, 9)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS)
|
||||
.ignorable()
|
||||
.partial(),
|
||||
.partial(), // Lots of weird interactions with moves and abilities such as negating status moves that target the field
|
||||
new Ability(Abilities.VESSEL_OF_RUIN, 9)
|
||||
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)
|
||||
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) }))
|
||||
@ -5960,7 +5969,7 @@ export function initAbilities() {
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
|
||||
new Ability(Abilities.SUPREME_OVERLORD, 9)
|
||||
.attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5))
|
||||
.partial(),
|
||||
.partial(), // Counter resets every wave
|
||||
new Ability(Abilities.COSTAR, 9)
|
||||
.attr(PostSummonCopyAllyStatsAbAttr),
|
||||
new Ability(Abilities.TOXIC_DEBRIS, 9)
|
||||
@ -5993,25 +6002,25 @@ export function initAbilities() {
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
.partial(), // Ogerpon tera interactions
|
||||
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
|
||||
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
.partial(), // Ogerpon tera interactions
|
||||
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
|
||||
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
.partial(), // Ogerpon tera interactions
|
||||
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
|
||||
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
.partial(), // Ogerpon tera interactions
|
||||
new Ability(Abilities.TERA_SHIFT, 9)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
@ -4,7 +4,7 @@ import { Type } from "#app/data/type";
|
||||
import * as Utils from "#app/utils";
|
||||
import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "#app/data/move";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import Pokemon, { HitResult, PlayerPokemon, PokemonMove, EnemyPokemon } from "#app/field/pokemon";
|
||||
import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon";
|
||||
import { StatusEffect } from "#app/data/status-effect";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
|
||||
@ -71,6 +71,32 @@ export abstract class ArenaTag {
|
||||
this.sourceId = source.sourceId;
|
||||
this.side = source.side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the source Pokemon
|
||||
* @param scene medium to retrieve the source Pokemon
|
||||
* @returns The source {@linkcode Pokemon} or `null` if none is found
|
||||
*/
|
||||
public getSourcePokemon(scene: BattleScene): Pokemon | null {
|
||||
return this.sourceId ? scene.getPokemonById(this.sourceId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the Pokemon affected
|
||||
* @param scene - medium to retrieve the involved Pokemon
|
||||
* @returns list of PlayerPokemon or EnemyPokemon on the field
|
||||
*/
|
||||
public getAffectedPokemon(scene: BattleScene): Pokemon[] {
|
||||
switch (this.side) {
|
||||
case ArenaTagSide.PLAYER:
|
||||
return scene.getPlayerField() ?? [];
|
||||
case ArenaTagSide.ENEMY:
|
||||
return scene.getEnemyField() ?? [];
|
||||
case ArenaTagSide.BOTH:
|
||||
default:
|
||||
return scene.getField(true) ?? [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -978,36 +1004,24 @@ class NoneTag extends ArenaTag {
|
||||
* Imprison will apply to any opposing Pokemon that switch onto the field as well.
|
||||
*/
|
||||
class ImprisonTag extends ArenaTrapTag {
|
||||
private source: Pokemon;
|
||||
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.IMPRISON, Moves.IMPRISON, sourceId, side, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the Pokemon affected
|
||||
* @param {BattleScene} scene medium to retrieve the involved Pokemon
|
||||
* @returns list of PlayerPokemon or EnemyPokemon on the field
|
||||
*/
|
||||
private retrieveField(scene: BattleScene): PlayerPokemon[] | EnemyPokemon[] {
|
||||
if (!this.source.isPlayer()) {
|
||||
return scene.getPlayerField() ?? [];
|
||||
}
|
||||
return scene.getEnemyField() ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function applies the effects of Imprison to the opposing Pokemon already present on the field.
|
||||
* @param arena
|
||||
*/
|
||||
override onAdd({ scene }: Arena) {
|
||||
this.source = scene.getPokemonById(this.sourceId!)!;
|
||||
if (this.source) {
|
||||
const party = this.retrieveField(scene);
|
||||
party?.forEach((p: PlayerPokemon | EnemyPokemon ) => {
|
||||
const source = this.getSourcePokemon(scene);
|
||||
if (source) {
|
||||
const party = this.getAffectedPokemon(scene);
|
||||
party?.forEach((p: Pokemon ) => {
|
||||
if (p.isAllowedInBattle()) {
|
||||
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
|
||||
}
|
||||
});
|
||||
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(this.source) }));
|
||||
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1016,8 +1030,9 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
* @param _arena
|
||||
* @returns `true` if the source of the tag is still active on the field | `false` if not
|
||||
*/
|
||||
override lapse(_arena: Arena): boolean {
|
||||
return this.source.isActive(true);
|
||||
override lapse({ scene }: Arena): boolean {
|
||||
const source = this.getSourcePokemon(scene);
|
||||
return source ? source.isActive(true) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1026,7 +1041,8 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
* @returns `true`
|
||||
*/
|
||||
override activateTrap(pokemon: Pokemon): boolean {
|
||||
if (this.source.isActive(true)) {
|
||||
const source = this.getSourcePokemon(pokemon.scene);
|
||||
if (source && source.isActive(true) && pokemon.isAllowedInBattle()) {
|
||||
pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
|
||||
}
|
||||
return true;
|
||||
@ -1037,8 +1053,8 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
* @param arena
|
||||
*/
|
||||
override onRemove({ scene }: Arena): void {
|
||||
const party = this.retrieveField(scene);
|
||||
party?.forEach((p: PlayerPokemon | EnemyPokemon) => {
|
||||
const party = this.getAffectedPokemon(scene);
|
||||
party?.forEach((p: Pokemon) => {
|
||||
p.removeTag(BattlerTagType.IMPRISON);
|
||||
});
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { StatStageChangePhase, StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
|
||||
import { PokemonAnimType } from "#app/enums/pokemon-anim-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
|
||||
export enum BattlerTagLapseType {
|
||||
FAINT,
|
||||
@ -90,6 +91,15 @@ export class BattlerTag {
|
||||
this.sourceMove = source.sourceMove;
|
||||
this.sourceId = source.sourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the source Pokemon object
|
||||
* @param scene medium to retrieve the source Pokemon
|
||||
* @returns The source {@linkcode Pokemon} or `null` if none is found
|
||||
*/
|
||||
public getSourcePokemon(scene: BattleScene): Pokemon | null {
|
||||
return this.sourceId ? scene.getPokemonById(this.sourceId) : null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface WeatherBattlerTag {
|
||||
@ -120,7 +130,7 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
|
||||
const phase = pokemon.scene.getCurrentPhase() as MovePhase;
|
||||
const move = phase.move;
|
||||
|
||||
if (this.isMoveRestricted(move.moveId)) {
|
||||
if (this.isMoveRestricted(move.moveId, pokemon)) {
|
||||
if (this.interruptedText(pokemon, move.moveId)) {
|
||||
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
|
||||
}
|
||||
@ -136,10 +146,11 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
|
||||
/**
|
||||
* Gets whether this tag is restricting a move.
|
||||
*
|
||||
* @param {Moves} move {@linkcode Moves} ID to check restriction for.
|
||||
* @returns {boolean} `true` if the move is restricted by this tag, otherwise `false`.
|
||||
* @param move - {@linkcode Moves} ID to check restriction for.
|
||||
* @param user - The {@linkcode Pokemon} involved
|
||||
* @returns `true` if the move is restricted by this tag, otherwise `false`.
|
||||
*/
|
||||
abstract isMoveRestricted(move: Moves): boolean;
|
||||
public abstract isMoveRestricted(move: Moves, user?: Pokemon): boolean;
|
||||
|
||||
/**
|
||||
* Checks if this tag is restricting a move based on a user's decisions during the target selection phase
|
||||
@ -327,6 +338,16 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
||||
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Gorilla Tactics Battler Tag along with its unique class variable moveId
|
||||
* @override
|
||||
* @param source Gorilla Tactics' {@linkcode BattlerTag} information
|
||||
*/
|
||||
public override loadTag(source: BattlerTag | any): void {
|
||||
super.loadTag(source);
|
||||
this.moveId = source.moveId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
@ -2510,8 +2531,6 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
|
||||
* Torment does not interrupt the move if the move is performed consecutively in the same turn and right after Torment is applied
|
||||
*/
|
||||
export class TormentTag extends MoveRestrictionBattlerTag {
|
||||
private target: Pokemon;
|
||||
|
||||
constructor(sourceId: number) {
|
||||
super(BattlerTagType.TORMENT, BattlerTagLapseType.AFTER_MOVE, 1, Moves.TORMENT, sourceId);
|
||||
}
|
||||
@ -2523,7 +2542,6 @@ export class TormentTag extends MoveRestrictionBattlerTag {
|
||||
*/
|
||||
override onAdd(pokemon: Pokemon) {
|
||||
super.onAdd(pokemon);
|
||||
this.target = pokemon;
|
||||
pokemon.scene.queueMessage(i18next.t("battlerTags:tormentOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), 1500);
|
||||
}
|
||||
|
||||
@ -2542,15 +2560,18 @@ export class TormentTag extends MoveRestrictionBattlerTag {
|
||||
* @param {Moves} move the move under investigation
|
||||
* @returns `true` if there is valid consecutive usage | `false` if the moves are different from each other
|
||||
*/
|
||||
override isMoveRestricted(move: Moves): boolean {
|
||||
const lastMove = this.target.getLastXMoves(1)[0];
|
||||
public override isMoveRestricted(move: Moves, user: Pokemon): boolean {
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
const lastMove = user.getLastXMoves(1)[0];
|
||||
if ( !lastMove ) {
|
||||
return false;
|
||||
}
|
||||
// This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY
|
||||
// Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts
|
||||
const moveObj = allMoves[lastMove.move];
|
||||
const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || this.target.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr);
|
||||
const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || user.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr);
|
||||
const validLastMoveResult = (lastMove.result === MoveResult.SUCCESS) || (lastMove.result === MoveResult.MISS);
|
||||
if (lastMove.move === move && validLastMoveResult && lastMove.move !== Moves.STRUGGLE && !isUnaffected) {
|
||||
return true;
|
||||
@ -2602,37 +2623,39 @@ export class TauntTag extends MoveRestrictionBattlerTag {
|
||||
* The tag is only removed when the source-user is removed from the field.
|
||||
*/
|
||||
export class ImprisonTag extends MoveRestrictionBattlerTag {
|
||||
private source: Pokemon | null;
|
||||
|
||||
constructor(sourceId: number) {
|
||||
super(BattlerTagType.IMPRISON, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 1, Moves.IMPRISON, sourceId);
|
||||
}
|
||||
|
||||
override onAdd(pokemon: Pokemon) {
|
||||
if (this.sourceId) {
|
||||
this.source = pokemon.scene.getPokemonById(this.sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the source of Imprison is still active
|
||||
* @param _pokemon
|
||||
* @param _lapseType
|
||||
* @override
|
||||
* @param pokemon The pokemon this tag is attached to
|
||||
* @returns `true` if the source is still active
|
||||
*/
|
||||
override lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean {
|
||||
return this.source?.isActive(true) ?? false;
|
||||
public override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const source = this.getSourcePokemon(pokemon.scene);
|
||||
if (source) {
|
||||
if (lapseType === BattlerTagLapseType.PRE_MOVE) {
|
||||
return super.lapse(pokemon, lapseType) && source.isActive(true);
|
||||
} else {
|
||||
return source.isActive(true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the source of the tag has the parameter move in its moveset and that the source is still active
|
||||
* @override
|
||||
* @param {Moves} move the move under investigation
|
||||
* @returns `false` if either condition is not met
|
||||
*/
|
||||
override isMoveRestricted(move: Moves): boolean {
|
||||
if (this.source) {
|
||||
const sourceMoveset = this.source.getMoveset().map(m => m!.moveId);
|
||||
return sourceMoveset?.includes(move) && this.source.isActive(true);
|
||||
public override isMoveRestricted(move: Moves, user: Pokemon): boolean {
|
||||
const source = this.getSourcePokemon(user.scene);
|
||||
if (source) {
|
||||
const sourceMoveset = source.getMoveset().map(m => m!.moveId);
|
||||
return sourceMoveset?.includes(move) && source.isActive(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -365,6 +365,14 @@ export default class Move implements Localizable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal dev flag for documenting edge cases. When using this, please document the known edge case.
|
||||
* @returns the called object {@linkcode Move}
|
||||
*/
|
||||
edgeCase(): this {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the move as "partial": appends texts to the move name
|
||||
* @returns the called object {@linkcode Move}
|
||||
@ -7086,7 +7094,8 @@ export function initMoves() {
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.ignoresSubstitute()
|
||||
.hidesTarget()
|
||||
.windMove(),
|
||||
.windMove()
|
||||
.partial(), // Should force random switches
|
||||
new AttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1)
|
||||
.attr(ChargeAttr, ChargeAnim.FLY_CHARGING, i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" }), BattlerTagType.FLYING)
|
||||
.condition(failOnGravityCondition)
|
||||
@ -7163,7 +7172,8 @@ export function initMoves() {
|
||||
new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.soundBased()
|
||||
.hidesTarget(),
|
||||
.hidesTarget()
|
||||
.partial(), // Should force random switching
|
||||
new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1)
|
||||
.attr(StatusEffectAttr, StatusEffect.SLEEP)
|
||||
.soundBased(),
|
||||
@ -7304,7 +7314,7 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
|
||||
new AttackMove(Moves.QUICK_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1),
|
||||
new AttackMove(Moves.RAGE, Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1)
|
||||
.partial(),
|
||||
.partial(), // No effect implemented
|
||||
new SelfStatusMove(Moves.TELEPORT, Type.PSYCHIC, -1, 20, -1, -6, 1)
|
||||
.attr(ForceSwitchOutAttr, true)
|
||||
.hidesUser(),
|
||||
@ -7619,7 +7629,7 @@ export function initMoves() {
|
||||
new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK ], -2),
|
||||
new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2)
|
||||
.partial()
|
||||
.partial() // Does not lock the user, also does not increase damage properly
|
||||
.attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL),
|
||||
new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2)
|
||||
.attr(SurviveDamageAttr),
|
||||
@ -7690,7 +7700,7 @@ export function initMoves() {
|
||||
.ignoresSubstitute()
|
||||
.condition((user, target, move) => new EncoreTag(user.id).canAdd(target)),
|
||||
new AttackMove(Moves.PURSUIT, Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2)
|
||||
.partial(),
|
||||
.partial(), // No effect implemented
|
||||
new AttackMove(Moves.RAPID_SPIN, Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 1, true)
|
||||
.attr(RemoveBattlerTagAttr, [
|
||||
@ -7755,7 +7765,7 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -1)
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.FUTURE_SIGHT, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2)
|
||||
.partial()
|
||||
.partial() // Complete buggy mess
|
||||
.attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, i18next.t("moveTriggers:foresawAnAttack", { pokemonName: "{USER}" })),
|
||||
new AttackMove(Moves.ROCK_SMASH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2)
|
||||
.attr(StatStageChangeAttr, [ Stat.DEF ], -1),
|
||||
@ -7773,7 +7783,7 @@ export function initMoves() {
|
||||
.ignoresVirtual()
|
||||
.soundBased()
|
||||
.target(MoveTarget.RANDOM_NEAR_ENEMY)
|
||||
.partial(),
|
||||
.partial(), // Does not lock the user, does not stop Pokemon from sleeping
|
||||
new SelfStatusMove(Moves.STOCKPILE, Type.NORMAL, -1, 20, -1, 0, 3)
|
||||
.condition(user => (user.getTag(StockpilingTag)?.stockpiledCount ?? 0) < 3)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.STOCKPILING, true),
|
||||
@ -7796,7 +7806,7 @@ export function initMoves() {
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3)
|
||||
.ignoresSubstitute()
|
||||
.partial() // Incomplete implementation because of Uproar's partial implementation
|
||||
.edgeCase() // Incomplete implementation because of Uproar's partial implementation
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.TORMENT, false, true, 1),
|
||||
new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPATK ], 1)
|
||||
@ -7875,7 +7885,8 @@ export function initMoves() {
|
||||
.attr(SwitchAbilitiesAttr),
|
||||
new StatusMove(Moves.IMPRISON, Type.PSYCHIC, 100, 10, -1, 0, 3)
|
||||
.ignoresSubstitute()
|
||||
.attr(AddArenaTagAttr, ArenaTagType.IMPRISON, 1, true, false),
|
||||
.attr(AddArenaTagAttr, ArenaTagType.IMPRISON, 1, true, false)
|
||||
.target(MoveTarget.ENEMY_SIDE),
|
||||
new SelfStatusMove(Moves.REFRESH, Type.NORMAL, -1, 20, -1, 0, 3)
|
||||
.attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN)
|
||||
.condition((user, target, move) => !!user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)),
|
||||
@ -7885,7 +7896,7 @@ export function initMoves() {
|
||||
.unimplemented(),
|
||||
new AttackMove(Moves.SECRET_POWER, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, 30, 0, 3)
|
||||
.makesContact(false)
|
||||
.partial(),
|
||||
.partial(), // No effect implemented
|
||||
new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3)
|
||||
.attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", { pokemonName: "{USER}" }), BattlerTagType.UNDERWATER, true)
|
||||
.attr(GulpMissileTagAttr)
|
||||
@ -7916,7 +7927,7 @@ export function initMoves() {
|
||||
.attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new AttackMove(Moves.ICE_BALL, Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 3)
|
||||
.partial()
|
||||
.partial() // Does not lock the user properly, does not increase damage correctly
|
||||
.attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL)
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3)
|
||||
@ -8059,7 +8070,7 @@ export function initMoves() {
|
||||
.attr(ConfuseAttr)
|
||||
.pulseMove(),
|
||||
new AttackMove(Moves.DOOM_DESIRE, Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3)
|
||||
.partial()
|
||||
.partial() // Complete buggy mess
|
||||
.attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, i18next.t("moveTriggers:choseDoomDesireAsDestiny", { pokemonName: "{USER}" })),
|
||||
new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true),
|
||||
@ -8469,7 +8480,7 @@ export function initMoves() {
|
||||
.attr(AfterYouAttr),
|
||||
new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
|
||||
.soundBased()
|
||||
.partial(),
|
||||
.partial(), // No effect implemented
|
||||
new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5)
|
||||
.attr(ConsecutiveUseMultiBasePowerAttr, 5, false)
|
||||
.soundBased(),
|
||||
@ -8511,7 +8522,8 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK ], 1, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
|
||||
new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
|
||||
.attr(ForceSwitchOutAttr),
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.partial(), // Should force random switches
|
||||
new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||
.attr(RemoveHeldItemAttr, true),
|
||||
@ -8579,7 +8591,8 @@ export function initMoves() {
|
||||
.attr(CritOnlyAttr),
|
||||
new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.hidesTarget(),
|
||||
.hidesTarget()
|
||||
.partial(), // Should force random switches
|
||||
new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true),
|
||||
new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5)
|
||||
@ -8707,7 +8720,7 @@ export function initMoves() {
|
||||
.ignoresVirtual(),
|
||||
new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6)
|
||||
.attr(AddTypeAttr, Type.GHOST)
|
||||
.partial(),
|
||||
.edgeCase(), // Weird interaction with Forest's Curse, reflect type, burn up
|
||||
new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1)
|
||||
.soundBased(),
|
||||
@ -8720,7 +8733,7 @@ export function initMoves() {
|
||||
.triageMove(),
|
||||
new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6)
|
||||
.attr(AddTypeAttr, Type.GRASS)
|
||||
.partial(),
|
||||
.edgeCase(), // Weird interaction with Trick or Treat, reflect type, burn up
|
||||
new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6)
|
||||
.windMove()
|
||||
.makesContact(false)
|
||||
@ -8728,7 +8741,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
|
||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||
.attr(WaterSuperEffectTypeMultiplierAttr)
|
||||
.partial(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell.
|
||||
.edgeCase(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell.
|
||||
new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
|
||||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
@ -9106,15 +9119,15 @@ export function initMoves() {
|
||||
/* Unused */
|
||||
new AttackMove(Moves.SINISTER_ARROW_RAID, Type.GHOST, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
|
||||
.makesContact(false)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because the user needs spirit shackle and decidueye
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
|
||||
.attr(AlwaysHitMinimizeAttr)
|
||||
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because it needs darkest lariat and incineroar
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because it needs sparkling aria and primarina
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.GUARDIAN_OF_ALOLA, Type.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7)
|
||||
.unimplemented()
|
||||
@ -9123,10 +9136,10 @@ export function initMoves() {
|
||||
.unimplemented()
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.STOKED_SPARKSURFER, Type.ELECTRIC, MoveCategory.SPECIAL, 175, -1, 1, 100, 0, 7)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because it needs thunderbolt and Alola Raichu
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.PULVERIZING_PANCAKE, Type.NORMAL, MoveCategory.PHYSICAL, 210, -1, 1, -1, 0, 7)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because it needs giga impact and snorlax
|
||||
.ignoresVirtual(),
|
||||
new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, -1, 0, 7)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true)
|
||||
@ -9157,13 +9170,13 @@ export function initMoves() {
|
||||
.attr(RechargeAttr),
|
||||
new AttackMove(Moves.SPECTRAL_THIEF, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7)
|
||||
.ignoresSubstitute()
|
||||
.partial(),
|
||||
.partial(), // Does not steal stats
|
||||
new AttackMove(Moves.SUNSTEEL_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7)
|
||||
.ignoresAbilities()
|
||||
.partial(),
|
||||
.edgeCase(), // Should not ignore abilities when called virtually (metronome)
|
||||
new AttackMove(Moves.MOONGEIST_BEAM, Type.GHOST, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
|
||||
.ignoresAbilities()
|
||||
.partial(),
|
||||
.edgeCase(), // Should not ignore abilities when called virtually (metronome)
|
||||
new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1),
|
||||
new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7)
|
||||
@ -9174,7 +9187,7 @@ export function initMoves() {
|
||||
.attr(FormChangeItemTypeAttr),
|
||||
/* Unused */
|
||||
new AttackMove(Moves.TEN_MILLION_VOLT_THUNDERBOLT, Type.ELECTRIC, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7)
|
||||
.partial()
|
||||
.edgeCase() // I assume it's because it needs thunderbolt and pikachu in a cap
|
||||
.ignoresVirtual(),
|
||||
/* End Unused */
|
||||
new AttackMove(Moves.MIND_BLOWN, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 7)
|
||||
@ -9187,7 +9200,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
|
||||
.attr(PhotonGeyserCategoryAttr)
|
||||
.ignoresAbilities()
|
||||
.partial(),
|
||||
.edgeCase(), // Should not ignore abilities when called virtually (metronome)
|
||||
/* Unused */
|
||||
new AttackMove(Moves.LIGHT_THAT_BURNS_THE_SKY, Type.PSYCHIC, MoveCategory.SPECIAL, 200, -1, 1, -1, 0, 7)
|
||||
.attr(PhotonGeyserCategoryAttr)
|
||||
@ -9200,7 +9213,7 @@ export function initMoves() {
|
||||
.ignoresAbilities()
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.LETS_SNUGGLE_FOREVER, Type.FAIRY, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7)
|
||||
.partial()
|
||||
.edgeCase() // I assume it needs play rough and mimikyu
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.SPLINTERED_STORMSHARDS, Type.ROCK, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7)
|
||||
.attr(ClearTerrainAttr)
|
||||
@ -9210,7 +9223,7 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true)
|
||||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||
.partial()
|
||||
.edgeCase() // I assume it needs clanging scales and Kommo-O
|
||||
.ignoresVirtual(),
|
||||
/* End Unused */
|
||||
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation
|
||||
@ -9273,14 +9286,14 @@ export function initMoves() {
|
||||
new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8)
|
||||
.attr(JawLockAttr)
|
||||
.bitingMove(),
|
||||
new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) // TODO: Stuff Cheeks should not be selectable when the user does not have a berry, see wiki
|
||||
new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8)
|
||||
.attr(EatBerryAttr)
|
||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true)
|
||||
.condition((user) => {
|
||||
const userBerries = user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer());
|
||||
return userBerries.length > 0;
|
||||
})
|
||||
.partial(),
|
||||
.edgeCase(), // Stuff Cheeks should not be selectable when the user does not have a berry, see wiki
|
||||
new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, -1, 0, 8)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false)
|
||||
@ -9294,7 +9307,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.DRAGON_DARTS, Type.DRAGON, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 8)
|
||||
.attr(MultiHitAttr, MultiHitType._2)
|
||||
.makesContact(false)
|
||||
.partial(),
|
||||
.partial(), // smart targetting is unimplemented
|
||||
new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8)
|
||||
.attr(EatBerryAttr)
|
||||
.target(MoveTarget.ALL),
|
||||
@ -9733,7 +9746,7 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -2),
|
||||
new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9)
|
||||
.makesContact(false)
|
||||
.partial(),
|
||||
.partial(), // No effect implemented (requires Commander)
|
||||
new AttackMove(Moves.JET_PUNCH, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9)
|
||||
.punchingMove(),
|
||||
new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9)
|
||||
@ -9945,8 +9958,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9)
|
||||
.attr(FlinchAttr)
|
||||
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].priority > 0 ) // TODO: is this bang correct?
|
||||
//TODO: Should also apply when target move priority increased by ability ex. gale wings
|
||||
.partial(),
|
||||
.partial(), // Should also apply when target move priority increased by ability ex. gale wings
|
||||
new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9)
|
||||
.attr(StatusEffectAttr, StatusEffect.TOXIC)
|
||||
);
|
||||
|
@ -975,7 +975,7 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.VENUSAUR, 1, false, false, false, "Seed Pokémon", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, GrowthRate.MEDIUM_SLOW, 87.5, true, true,
|
||||
new PokemonForm("Normal", "", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, true, null, true),
|
||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GRASS, Type.POISON, 2.4, 155.5, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 625, 80, 100, 123, 122, 120, 80, 45, 50, 263, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.EFFECT_SPORE, Abilities.EFFECT_SPORE, Abilities.EFFECT_SPORE, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.EFFECT_SPORE, Abilities.NONE, Abilities.EFFECT_SPORE, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true),
|
||||
),
|
||||
new PokemonSpecies(Species.CHARMANDER, 1, false, false, false, "Lizard Pokémon", Type.FIRE, null, 0.6, 8.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 309, 39, 52, 43, 60, 50, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.CHARMELEON, 1, false, false, false, "Flame Pokémon", Type.FIRE, null, 1.1, 19, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 405, 58, 64, 58, 80, 65, 80, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
@ -983,20 +983,20 @@ export function initSpecies() {
|
||||
new PokemonForm("Normal", "", Type.FIRE, Type.FLYING, 1.7, 90.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 534, 78, 84, 78, 109, 85, 100, 45, 50, 267, false, null, true),
|
||||
new PokemonForm("Mega X", SpeciesFormKey.MEGA_X, Type.FIRE, Type.DRAGON, 1.7, 110.5, Abilities.TOUGH_CLAWS, Abilities.NONE, Abilities.TOUGH_CLAWS, 634, 78, 130, 111, 130, 85, 100, 45, 50, 267),
|
||||
new PokemonForm("Mega Y", SpeciesFormKey.MEGA_Y, Type.FIRE, Type.FLYING, 1.7, 100.5, Abilities.DROUGHT, Abilities.NONE, Abilities.DROUGHT, 634, 78, 104, 78, 159, 115, 100, 45, 50, 267),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.BERSERK, Abilities.BERSERK, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.NONE, Abilities.BERSERK, 634, 118, 84, 93, 139, 100, 100, 45, 50, 267),
|
||||
),
|
||||
new PokemonSpecies(Species.SQUIRTLE, 1, false, false, false, "Tiny Turtle Pokémon", Type.WATER, null, 0.5, 9, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 314, 44, 48, 65, 50, 64, 43, 45, 50, 63, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.WARTORTLE, 1, false, false, false, "Turtle Pokémon", Type.WATER, null, 1, 22.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 405, 59, 63, 80, 65, 80, 58, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.BLASTOISE, 1, false, false, false, "Shellfish Pokémon", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||
new PokemonForm("Normal", "", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, false, null, true),
|
||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.WATER, null, 1.6, 101.1, Abilities.MEGA_LAUNCHER, Abilities.NONE, Abilities.MEGA_LAUNCHER, 630, 79, 103, 120, 135, 115, 78, 45, 50, 265),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 135, 115, 110, 68, 45, 50, 265),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.NONE, Abilities.SHELL_ARMOR, 630, 119, 83, 135, 115, 110, 68, 45, 50, 265),
|
||||
),
|
||||
new PokemonSpecies(Species.CATERPIE, 1, false, false, false, "Worm Pokémon", Type.BUG, null, 0.3, 2.9, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 45, 30, 35, 20, 20, 45, 255, 50, 39, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true,
|
||||
new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
|
||||
),
|
||||
new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
@ -1025,7 +1025,7 @@ export function initSpecies() {
|
||||
new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
|
||||
new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
|
||||
new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form
|
||||
),
|
||||
new PokemonSpecies(Species.RAICHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.8, 30, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 485, 60, 90, 55, 90, 80, 110, 75, 50, 243, GrowthRate.MEDIUM_FAST, 50, true),
|
||||
new PokemonSpecies(Species.SANDSHREW, 1, false, false, false, "Mouse Pokémon", Type.GROUND, null, 0.6, 12, Abilities.SAND_VEIL, Abilities.NONE, Abilities.SAND_RUSH, 300, 50, 75, 85, 20, 30, 40, 255, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
@ -1110,7 +1110,7 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.GENGAR, 1, false, false, false, "Shadow Pokémon", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, GrowthRate.MEDIUM_SLOW, 50, false, true,
|
||||
new PokemonForm("Normal", "", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, false, null, true),
|
||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GHOST, Type.POISON, 1.4, 40.5, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.NONE, 600, 60, 65, 80, 170, 95, 130, 45, 50, 250),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250),
|
||||
),
|
||||
new PokemonSpecies(Species.ONIX, 1, false, false, false, "Rock Snake Pokémon", Type.ROCK, Type.GROUND, 8.8, 210, Abilities.ROCK_HEAD, Abilities.STURDY, Abilities.WEAK_ARMOR, 385, 35, 45, 160, 30, 45, 70, 45, 50, 77, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.DROWZEE, 1, false, false, false, "Hypnosis Pokémon", Type.PSYCHIC, null, 1, 32.4, Abilities.INSOMNIA, Abilities.FOREWARN, Abilities.INNER_FOCUS, 328, 60, 48, 45, 43, 90, 42, 190, 70, 66, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
@ -1118,7 +1118,7 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.KRABBY, 1, false, false, false, "River Crab Pokémon", Type.WATER, null, 0.4, 6.5, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 325, 30, 105, 90, 25, 25, 50, 225, 50, 65, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.KINGLER, 1, false, false, false, "Pincer Pokémon", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true,
|
||||
new PokemonForm("Normal", "", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 70, 70, 60, 50, 166),
|
||||
),
|
||||
new PokemonSpecies(Species.VOLTORB, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 0.5, 10.4, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 330, 40, 30, 50, 55, 55, 100, 190, 70, 66, GrowthRate.MEDIUM_FAST, null, false),
|
||||
new PokemonSpecies(Species.ELECTRODE, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 1.2, 66.6, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 490, 60, 50, 70, 80, 80, 150, 60, 70, 172, GrowthRate.MEDIUM_FAST, null, false),
|
||||
@ -2298,25 +2298,25 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.MELTAN, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 0.2, 8, Abilities.MAGNET_PULL, Abilities.NONE, Abilities.NONE, 300, 46, 65, 65, 55, 35, 34, 3, 0, 150, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(Species.MELMETAL, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, GrowthRate.SLOW, null, false, true,
|
||||
new PokemonForm("Normal", "", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300),
|
||||
),
|
||||
new PokemonSpecies(Species.GROOKEY, 8, false, false, false, "Chimp Pokémon", Type.GRASS, null, 0.3, 5, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 310, 50, 65, 50, 40, 40, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||
new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.NONE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265),
|
||||
),
|
||||
new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||
new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.NONE, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265),
|
||||
),
|
||||
new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||
new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.NONE, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265),
|
||||
),
|
||||
new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
@ -2422,7 +2422,7 @@ export function initSpecies() {
|
||||
new PokemonForm("Ruby Swirl", "ruby-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
|
||||
new PokemonForm("Caramel Swirl", "caramel-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
|
||||
new PokemonForm("Rainbow Swirl", "rainbow-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.NONE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173),
|
||||
),
|
||||
new PokemonSpecies(Species.FALINKS, 8, false, false, false, "Formation Pokémon", Type.FIGHTING, null, 3, 62, Abilities.BATTLE_ARMOR, Abilities.NONE, Abilities.DEFIANT, 470, 65, 100, 100, 70, 60, 75, 45, 50, 165, GrowthRate.MEDIUM_FAST, null, false),
|
||||
new PokemonSpecies(Species.PINCURCHIN, 8, false, false, false, "Sea Urchin Pokémon", Type.ELECTRIC, null, 0.3, 1, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.ELECTRIC_SURGE, 435, 48, 101, 95, 91, 85, 15, 75, 50, 152, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
@ -2444,7 +2444,7 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.CUFANT, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 1.2, 100, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 330, 72, 80, 49, 40, 49, 40, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.COPPERAJAH, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, true,
|
||||
new PokemonForm("Normal", "", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, false, null, true),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175),
|
||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.NONE, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175),
|
||||
),
|
||||
new PokemonSpecies(Species.DRACOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.DRAGON, 1.8, 190, Abilities.VOLT_ABSORB, Abilities.HUSTLE, Abilities.SAND_RUSH, 505, 90, 100, 90, 80, 70, 75, 45, 50, 177, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(Species.ARCTOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.ICE, 2.3, 150, Abilities.VOLT_ABSORB, Abilities.STATIC, Abilities.SLUSH_RUSH, 505, 90, 100, 90, 90, 80, 55, 45, 50, 177, GrowthRate.SLOW, null, false),
|
||||
|
@ -896,9 +896,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const relX = newOffset[0] - initialOffset[0];
|
||||
const relY = newOffset[1] - initialOffset[1];
|
||||
|
||||
const subTag = this.getTag(SubstituteTag);
|
||||
|
||||
if (duration) {
|
||||
// TODO: can this use stricter typing?
|
||||
const targets: any[] = [ this ];
|
||||
if (subTag?.sprite) {
|
||||
targets.push(subTag.sprite);
|
||||
}
|
||||
this.scene.tweens.add({
|
||||
targets: this,
|
||||
targets: targets,
|
||||
x: (_target, _key, value: number) => value + relX,
|
||||
y: (_target, _key, value: number) => value + relY,
|
||||
duration: duration,
|
||||
@ -908,6 +915,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
} else {
|
||||
this.x += relX;
|
||||
this.y += relY;
|
||||
if (subTag?.sprite) {
|
||||
subTag.sprite.x += relX;
|
||||
subTag.sprite.y += relY;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -3246,8 +3257,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*
|
||||
* @see {@linkcode MoveRestrictionBattlerTag}
|
||||
*/
|
||||
isMoveRestricted(moveId: Moves): boolean {
|
||||
return this.getRestrictingTag(moveId) !== null;
|
||||
public isMoveRestricted(moveId: Moves, pokemon?: Pokemon): boolean {
|
||||
return this.getRestrictingTag(moveId, pokemon) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3280,7 +3291,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
getRestrictingTag(moveId: Moves, user?: Pokemon, target?: Pokemon): MoveRestrictionBattlerTag | null {
|
||||
for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) {
|
||||
if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId)) {
|
||||
if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId, user)) {
|
||||
return tag as MoveRestrictionBattlerTag;
|
||||
} else if (user && target && (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) {
|
||||
return tag as MoveRestrictionBattlerTag;
|
||||
@ -5412,7 +5423,7 @@ export class PokemonMove {
|
||||
* @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`.
|
||||
*/
|
||||
isUsable(pokemon: Pokemon, ignorePp: boolean = false, ignoreRestrictionTags: boolean = false): boolean {
|
||||
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) {
|
||||
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ export class Phase {
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log(`%cStart Phase ${this.constructor.name}`, "color:green;");
|
||||
if (this.scene.abilityBar.shown) {
|
||||
this.scene.abilityBar.resetAutoHideTimer();
|
||||
}
|
||||
|
@ -114,8 +114,8 @@ export class CommandPhase extends FieldPhase {
|
||||
|
||||
// Decides between a Disabled, Not Implemented, or No PP translation message
|
||||
const errorMessage =
|
||||
playerPokemon.isMoveRestricted(move.moveId)
|
||||
? playerPokemon.getRestrictingTag(move.moveId)!.selectionDeniedText(playerPokemon, move.moveId)
|
||||
playerPokemon.isMoveRestricted(move.moveId, playerPokemon)
|
||||
? playerPokemon.getRestrictingTag(move.moveId, playerPokemon)!.selectionDeniedText(playerPokemon, move.moveId)
|
||||
: move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP";
|
||||
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
|
||||
|
||||
|
@ -36,6 +36,16 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
// Check if multiple stats are being changed at the same time, then run SSCPhase for each of them
|
||||
if (this.stats.length > 1) {
|
||||
for (let i = 0; i < this.stats.length; i++) {
|
||||
const stat = [ this.stats[i] ];
|
||||
this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.battlerIndex, this.selfTarget, stat, this.stages, this.showMessage, this.ignoreAbilities, this.canBeCopied, this.onChange));
|
||||
}
|
||||
return this.end();
|
||||
}
|
||||
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
if (!pokemon.isActive(true)) {
|
||||
|
72
src/test/abilities/competitive.test.ts
Normal file
72
src/test/abilities/competitive.test.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { Abilities } from "#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 } from "vitest";
|
||||
|
||||
describe("Abilities - Competitive", () => {
|
||||
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")
|
||||
.enemySpecies(Species.BEEDRILL)
|
||||
.enemyMoveset(Moves.TICKLE)
|
||||
.startingLevel(1)
|
||||
.moveset([ Moves.SPLASH, Moves.CLOSE_COMBAT ])
|
||||
.ability(Abilities.COMPETITIVE);
|
||||
});
|
||||
|
||||
it("lower atk and def by 1 via tickle, then increase spatk by 4 via competitive", async () => {
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(4);
|
||||
});
|
||||
|
||||
it("lowering your own stats should not trigger competitive", async () => {
|
||||
game.override.enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.CLOSE_COMBAT);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("white herb should remove only the negative effects", async () => {
|
||||
game.override.startingHeldItems([{ name: "WHITE_HERB" }]);
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(4);
|
||||
});
|
||||
});
|
70
src/test/abilities/defiant.test.ts
Normal file
70
src/test/abilities/defiant.test.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { Abilities } from "#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 } from "vitest";
|
||||
|
||||
describe("Abilities - Defiant", () => {
|
||||
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")
|
||||
.enemySpecies(Species.BEEDRILL)
|
||||
.enemyMoveset(Moves.TICKLE)
|
||||
.startingLevel(1)
|
||||
.moveset([ Moves.SPLASH, Moves.CLOSE_COMBAT ])
|
||||
.ability(Abilities.DEFIANT);
|
||||
});
|
||||
|
||||
it("lower atk and def by 1 via tickle, then increase atk by 4 via defiant", async () => {
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(3);
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
});
|
||||
|
||||
it("lowering your own stats should not trigger defiant", async () => {
|
||||
game.override.enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.CLOSE_COMBAT);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("white herb should remove only the negative effects", async () => {
|
||||
game.override.startingHeldItems([{ name: "WHITE_HERB" }]);
|
||||
await game.classicMode.startBattle([ Species.FLYGON ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(3);
|
||||
});
|
||||
});
|
@ -33,7 +33,7 @@ describe("Moves - Safeguard", () => {
|
||||
.enemyLevel(5)
|
||||
.starterSpecies(Species.DRATINI)
|
||||
.moveset([ Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
.ability(Abilities.UNNERVE); // Stop wild Pokemon from potentially eating Lum Berry
|
||||
});
|
||||
|
||||
it("protects from damaging moves with additional effects", async () => {
|
||||
|
@ -18,6 +18,7 @@ import { Moves } from "#enums/moves";
|
||||
import { ShinyRateBoosterModifier } from "#app/modifier/modifier";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import i18next from "i18next";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
|
||||
const namespace = "mysteryEncounters/anOfferYouCantRefuse";
|
||||
/** Gyarados for Indimidate */
|
||||
@ -37,10 +38,11 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
game.override.mysteryEncounterChance(100);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWaves();
|
||||
game.override.mysteryEncounterChance(100)
|
||||
.startingWave(defaultWave)
|
||||
.startingBiome(defaultBiome)
|
||||
.disableTrainerWaves()
|
||||
.ability(Abilities.INTIMIDATE); // Extortion ability
|
||||
|
||||
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
|
||||
[ Biome.VOLCANO, [ MysteryEncounterType.MYSTERIOUS_CHALLENGERS ]],
|
||||
@ -195,6 +197,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
|
||||
});
|
||||
|
||||
it("should award EXP to a pokemon with a move in EXTORTION_MOVES", async () => {
|
||||
game.override.ability(Abilities.SYNCHRONIZE); // Not an extortion ability, so we can test extortion move
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [ Species.ABRA ]);
|
||||
const party = scene.getParty();
|
||||
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!;
|
||||
|
@ -176,10 +176,12 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText");
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
||||
|
||||
scene.getParty().forEach(pkm => {
|
||||
vi.spyOn(pkm, "getStat").mockReturnValue(1); // for ease return for every stat
|
||||
});
|
||||
|
||||
const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
|
||||
const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId;
|
||||
// Setting enemy's level arbitrarily high to outspeed
|
||||
config.pokemonConfigs![0].dataSource!.level = 1000;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
|
||||
@ -198,10 +200,12 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText");
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
||||
|
||||
scene.getParty().forEach(pkm => {
|
||||
vi.spyOn(pkm, "getStat").mockReturnValue(1); // for ease return for every stat
|
||||
});
|
||||
|
||||
const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
|
||||
const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId;
|
||||
// Setting enemy's level arbitrarily high to outspeed
|
||||
config.pokemonConfigs![0].dataSource!.level = 1000;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
|
||||
|
@ -147,8 +147,8 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
||||
|
||||
expect(scene.getParty().length).toBe(initialPartySize + 1);
|
||||
|
||||
const newlyPurchasedPokemon = scene.getParty().find(p => p.name === pokemonName);
|
||||
expect(newlyPurchasedPokemon).toBeDefined();
|
||||
const newlyPurchasedPokemon = scene.getParty()[scene.getParty().length - 1];
|
||||
expect(newlyPurchasedPokemon.name).toBe(pokemonName);
|
||||
expect(newlyPurchasedPokemon!.moveset.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user