Merge branch 'beta' into moveRestrictionBugs

This commit is contained in:
NightKev 2024-10-12 22:29:01 -07:00 committed by GitHub
commit dc978c59e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 453 additions and 97 deletions

View File

@ -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 { overridePhase(phase: Phase): boolean {
@ -2326,6 +2329,7 @@ export default class BattleScene extends SceneBase {
this.standbyPhase = this.currentPhase; this.standbyPhase = this.currentPhase;
this.currentPhase = phase; this.currentPhase = phase;
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
phase.start(); phase.start();
return true; return true;

View File

@ -118,6 +118,14 @@ export class Ability implements Localizable {
this.nameAppend += " (N)"; this.nameAppend += " (N)";
return this; 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>; type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
@ -4906,7 +4914,7 @@ export function initAbilities() {
.ignorable(), .ignorable(),
new Ability(Abilities.SHIELD_DUST, 3) new Ability(Abilities.SHIELD_DUST, 3)
.attr(IgnoreMoveEffectsAbAttr) .attr(IgnoreMoveEffectsAbAttr)
.partial(), .edgeCase(), // Does not work with secret power (unimplemented)
new Ability(Abilities.OWN_TEMPO, 3) new Ability(Abilities.OWN_TEMPO, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED) .attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
.attr(IntimidateImmunityAbAttr) .attr(IntimidateImmunityAbAttr)
@ -4951,7 +4959,7 @@ export function initAbilities() {
.ignorable(), .ignorable(),
new Ability(Abilities.SERENE_GRACE, 3) new Ability(Abilities.SERENE_GRACE, 3)
.attr(MoveEffectChanceMultiplierAbAttr, 2) .attr(MoveEffectChanceMultiplierAbAttr, 2)
.partial(), .edgeCase(), // does not work with secret power (unimplemented)
new Ability(Abilities.SWIFT_SWIM, 3) new Ability(Abilities.SWIFT_SWIM, 3)
.attr(StatMultiplierAbAttr, Stat.SPD, 2) .attr(StatMultiplierAbAttr, Stat.SPD, 2)
.condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)),
@ -5235,7 +5243,8 @@ export function initAbilities() {
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)
.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) new Ability(Abilities.CONTRARY, 5)
.attr(StatStageChangeMultiplierAbAttr, -1) .attr(StatStageChangeMultiplierAbAttr, -1)
.ignorable(), .ignorable(),
@ -5278,7 +5287,7 @@ export function initAbilities() {
/** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */ /** 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) (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) new Ability(Abilities.TELEPATHY, 5)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
.ignorable(), .ignorable(),
@ -5357,7 +5366,7 @@ export function initAbilities() {
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.VICTORY_STAR, 5) new Ability(Abilities.VICTORY_STAR, 5)
.attr(StatMultiplierAbAttr, Stat.ACC, 1.1) .attr(StatMultiplierAbAttr, Stat.ACC, 1.1)
.partial(), .partial(), // Does not boost ally's accuracy
new Ability(Abilities.TURBOBLAZE, 5) new Ability(Abilities.TURBOBLAZE, 5)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(MoveAbilityBypassAbAttr), .attr(MoveAbilityBypassAbAttr),
@ -5468,7 +5477,7 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.bypassFaint() .bypassFaint()
.partial(), .partial(), // Meteor form should protect against status effects and yawn
new Ability(Abilities.STAKEOUT, 7) new Ability(Abilities.STAKEOUT, 7)
.attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2), .attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2),
new Ability(Abilities.WATER_BUBBLE, 7) new Ability(Abilities.WATER_BUBBLE, 7)
@ -5536,9 +5545,9 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.bypassFaint() .bypassFaint()
.partial(), .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 ]) .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) new Ability(Abilities.COMATOSE, 7)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
@ -5693,7 +5702,7 @@ export function initAbilities() {
new Ability(Abilities.WANDERING_SPIRIT, 8) new Ability(Abilities.WANDERING_SPIRIT, 8)
.attr(PostDefendAbilitySwapAbAttr) .attr(PostDefendAbilitySwapAbAttr)
.bypassFaint() .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) new Ability(Abilities.GORILLA_TACTICS, 8)
.attr(GorillaTacticsAbAttr), .attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8) new Ability(Abilities.NEUTRALIZING_GAS, 8)
@ -5702,7 +5711,7 @@ export function initAbilities() {
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.partial(), .partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed
new Ability(Abilities.PASTEL_VEIL, 8) new Ability(Abilities.PASTEL_VEIL, 8)
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
@ -5807,7 +5816,7 @@ export function initAbilities() {
new Ability(Abilities.GOOD_AS_GOLD, 9) new Ability(Abilities.GOOD_AS_GOLD, 9)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS)
.ignorable() .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) new Ability(Abilities.VESSEL_OF_RUIN, 9)
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75) .attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) })) .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) }))
@ -5840,7 +5849,7 @@ export function initAbilities() {
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
new Ability(Abilities.SUPREME_OVERLORD, 9) 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)) .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) new Ability(Abilities.COSTAR, 9)
.attr(PostSummonCopyAllyStatsAbAttr), .attr(PostSummonCopyAllyStatsAbAttr),
new Ability(Abilities.TOXIC_DEBRIS, 9) new Ability(Abilities.TOXIC_DEBRIS, 9)
@ -5873,25 +5882,25 @@ export function initAbilities() {
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9) new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9) new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9) new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
.attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.partial(), .partial(), // Ogerpon tera interactions
new Ability(Abilities.TERA_SHIFT, 9) new Ability(Abilities.TERA_SHIFT, 9)
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1) .attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)

View File

@ -911,7 +911,8 @@ export class GravityTag extends ArenaTag {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
arena.scene.getField(true).forEach((pokemon) => { arena.scene.getField(true).forEach((pokemon) => {
if (pokemon !== null) { if (pokemon !== null) {
pokemon.removeTag(BattlerTagType.MAGNET_RISEN); pokemon.removeTag(BattlerTagType.FLOATING);
pokemon.removeTag(BattlerTagType.TELEKINESIS);
} }
}); });
} }

View File

@ -1734,7 +1734,12 @@ export class TypeImmuneTag extends BattlerTag {
} }
} }
export class MagnetRisenTag extends TypeImmuneTag { /**
* Battler Tag that lifts the affected Pokemon into the air and provides immunity to Ground type moves.
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Magnet_Rise_(move) | Moves.MAGNET_RISE}
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS}
*/
export class FloatingTag extends TypeImmuneTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) { constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, sourceMove, Type.GROUND, 5); super(tagType, sourceMove, Type.GROUND, 5);
} }
@ -1742,14 +1747,18 @@ export class MagnetRisenTag extends TypeImmuneTag {
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
if (this.sourceMove === Moves.MAGNET_RISE) {
pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} }
}
onRemove(pokemon: Pokemon): void { onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon); super.onRemove(pokemon);
if (this.sourceMove === Moves.MAGNET_RISE) {
pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} }
}
} }
export class TypeBoostTag extends BattlerTag { export class TypeBoostTag extends BattlerTag {
@ -2699,6 +2708,22 @@ export class SyrupBombTag extends BattlerTag {
} }
} }
/**
* Telekinesis raises the target into the air for three turns and causes all moves used against the target (aside from OHKO moves) to hit the target unless the target is in a semi-invulnerable state from Fly/Dig.
* The first effect is provided by {@linkcode FloatingTag}, the accuracy-bypass effect is provided by TelekinesisTag
* The effects of Telekinesis can be baton passed to a teammate. Unlike the mainline games, Telekinesis can be baton-passed to Mega Gengar.
* @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS}
*/
export class TelekinesisTag extends BattlerTag {
constructor(sourceMove: Moves) {
super(BattlerTagType.TELEKINESIS, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 3, sourceMove, undefined, true);
}
override onAdd(pokemon: Pokemon) {
pokemon.scene.queueMessage(i18next.t("battlerTags:telekinesisOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
}
/** /**
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
* @param sourceId - The ID of the pokemon adding the tag * @param sourceId - The ID of the pokemon adding the tag
@ -2825,8 +2850,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new CursedTag(sourceId); return new CursedTag(sourceId);
case BattlerTagType.CHARGED: case BattlerTagType.CHARGED:
return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true); return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true);
case BattlerTagType.MAGNET_RISEN: case BattlerTagType.FLOATING:
return new MagnetRisenTag(tagType, sourceMove); return new FloatingTag(tagType, sourceMove);
case BattlerTagType.MINIMIZED: case BattlerTagType.MINIMIZED:
return new MinimizeTag(); return new MinimizeTag();
case BattlerTagType.DESTINY_BOND: case BattlerTagType.DESTINY_BOND:
@ -2872,6 +2897,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new ImprisonTag(sourceId); return new ImprisonTag(sourceId);
case BattlerTagType.SYRUP_BOMB: case BattlerTagType.SYRUP_BOMB:
return new SyrupBombTag(sourceId); return new SyrupBombTag(sourceId);
case BattlerTagType.TELEKINESIS:
return new TelekinesisTag(sourceMove);
case BattlerTagType.NONE: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -365,6 +365,14 @@ export default class Move implements Localizable {
return this; 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 * Marks the move as "partial": appends texts to the move name
* @returns the called object {@linkcode Move} * @returns the called object {@linkcode Move}
@ -7084,7 +7092,8 @@ export function initMoves() {
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr)
.ignoresSubstitute() .ignoresSubstitute()
.hidesTarget() .hidesTarget()
.windMove(), .windMove()
.partial(), // Should force random switches
new AttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1) 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) .attr(ChargeAttr, ChargeAnim.FLY_CHARGING, i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" }), BattlerTagType.FLYING)
.condition(failOnGravityCondition) .condition(failOnGravityCondition)
@ -7161,7 +7170,8 @@ export function initMoves() {
new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1) new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr)
.soundBased() .soundBased()
.hidesTarget(), .hidesTarget()
.partial(), // Should force random switching
new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1) new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1)
.attr(StatusEffectAttr, StatusEffect.SLEEP) .attr(StatusEffectAttr, StatusEffect.SLEEP)
.soundBased(), .soundBased(),
@ -7302,7 +7312,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
new AttackMove(Moves.QUICK_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1), 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) 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) new SelfStatusMove(Moves.TELEPORT, Type.PSYCHIC, -1, 20, -1, -6, 1)
.attr(ForceSwitchOutAttr, true) .attr(ForceSwitchOutAttr, true)
.hidesUser(), .hidesUser(),
@ -7617,7 +7627,7 @@ export function initMoves() {
new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2) new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2)
.attr(StatStageChangeAttr, [ Stat.ATK ], -2), .attr(StatStageChangeAttr, [ Stat.ATK ], -2),
new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 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), .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL),
new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2) new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2)
.attr(SurviveDamageAttr), .attr(SurviveDamageAttr),
@ -7688,7 +7698,7 @@ export function initMoves() {
.ignoresSubstitute() .ignoresSubstitute()
.condition((user, target, move) => new EncoreTag(user.id).canAdd(target)), .condition((user, target, move) => new EncoreTag(user.id).canAdd(target)),
new AttackMove(Moves.PURSUIT, Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2) 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) new AttackMove(Moves.RAPID_SPIN, Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2)
.attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true)
.attr(RemoveBattlerTagAttr, [ .attr(RemoveBattlerTagAttr, [
@ -7753,7 +7763,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1)
.ballBombMove(), .ballBombMove(),
new AttackMove(Moves.FUTURE_SIGHT, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) 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}" })), .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) new AttackMove(Moves.ROCK_SMASH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2)
.attr(StatStageChangeAttr, [ Stat.DEF ], -1), .attr(StatStageChangeAttr, [ Stat.DEF ], -1),
@ -7771,7 +7781,7 @@ export function initMoves() {
.ignoresVirtual() .ignoresVirtual()
.soundBased() .soundBased()
.target(MoveTarget.RANDOM_NEAR_ENEMY) .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) new SelfStatusMove(Moves.STOCKPILE, Type.NORMAL, -1, 20, -1, 0, 3)
.condition(user => (user.getTag(StockpilingTag)?.stockpiledCount ?? 0) < 3) .condition(user => (user.getTag(StockpilingTag)?.stockpiledCount ?? 0) < 3)
.attr(AddBattlerTagAttr, BattlerTagType.STOCKPILING, true), .attr(AddBattlerTagAttr, BattlerTagType.STOCKPILING, true),
@ -7794,7 +7804,7 @@ export function initMoves() {
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3) new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3)
.ignoresSubstitute() .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), .attr(AddBattlerTagAttr, BattlerTagType.TORMENT, false, true, 1),
new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3) new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3)
.attr(StatStageChangeAttr, [ Stat.SPATK ], 1) .attr(StatStageChangeAttr, [ Stat.SPATK ], 1)
@ -7843,7 +7853,9 @@ export function initMoves() {
.attr(RandomMovesetMoveAttr, true) .attr(RandomMovesetMoveAttr, true)
.ignoresVirtual(), .ignoresVirtual(),
new SelfStatusMove(Moves.INGRAIN, Type.GRASS, -1, 20, -1, 0, 3) new SelfStatusMove(Moves.INGRAIN, Type.GRASS, -1, 20, -1, 0, 3)
.attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true), .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, true)
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLOATING ], true),
new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true),
new SelfStatusMove(Moves.MAGIC_COAT, Type.PSYCHIC, -1, 15, -1, 4, 3) new SelfStatusMove(Moves.MAGIC_COAT, Type.PSYCHIC, -1, 15, -1, 4, 3)
@ -7882,7 +7894,7 @@ export function initMoves() {
.unimplemented(), .unimplemented(),
new AttackMove(Moves.SECRET_POWER, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, 30, 0, 3) new AttackMove(Moves.SECRET_POWER, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, 30, 0, 3)
.makesContact(false) .makesContact(false)
.partial(), .partial(), // No effect implemented
new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3) 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(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", { pokemonName: "{USER}" }), BattlerTagType.UNDERWATER, true)
.attr(GulpMissileTagAttr) .attr(GulpMissileTagAttr)
@ -7913,7 +7925,7 @@ export function initMoves() {
.attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5) .attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5)
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.ICE_BALL, Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 3) 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) .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL)
.ballBombMove(), .ballBombMove(),
new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3) new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3)
@ -8056,7 +8068,7 @@ export function initMoves() {
.attr(ConfuseAttr) .attr(ConfuseAttr)
.pulseMove(), .pulseMove(),
new AttackMove(Moves.DOOM_DESIRE, Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3) 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}" })), .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) new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3)
.attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true),
@ -8178,8 +8190,8 @@ export function initMoves() {
new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4) new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true),
new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.MAGNET_RISEN, true, true) .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, true, true)
.condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.MAGNET_RISEN, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))), .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.FLOATING, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))),
new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4) new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4)
.attr(RecoilAttr, false, 0.33) .attr(RecoilAttr, false, 0.33)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
@ -8404,7 +8416,11 @@ export function initMoves() {
.attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true), .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true),
new StatusMove(Moves.TELEKINESIS, Type.PSYCHIC, -1, 15, -1, 0, 5) new StatusMove(Moves.TELEKINESIS, Type.PSYCHIC, -1, 15, -1, 0, 5)
.condition(failOnGravityCondition) .condition(failOnGravityCondition)
.unimplemented(), .condition((_user, target, _move) => ![ Species.DIGLETT, Species.DUGTRIO, Species.ALOLA_DIGLETT, Species.ALOLA_DUGTRIO, Species.SANDYGAST, Species.PALOSSAND, Species.WIGLETT, Species.WUGTRIO ].includes(target.species.speciesId))
.condition((_user, target, _move) => !(target.species.speciesId === Species.GENGAR && target.getFormKey() === "mega"))
.condition((_user, target, _move) => Utils.isNullOrUndefined(target.getTag(BattlerTagType.INGRAIN)) && Utils.isNullOrUndefined(target.getTag(BattlerTagType.IGNORE_FLYING)))
.attr(AddBattlerTagAttr, BattlerTagType.TELEKINESIS, false, true, 3)
.attr(AddBattlerTagAttr, BattlerTagType.FLOATING, false, true, 3),
new StatusMove(Moves.MAGIC_ROOM, Type.PSYCHIC, -1, 10, -1, 0, 5) new StatusMove(Moves.MAGIC_ROOM, Type.PSYCHIC, -1, 10, -1, 0, 5)
.ignoresProtect() .ignoresProtect()
.target(MoveTarget.BOTH_SIDES) .target(MoveTarget.BOTH_SIDES)
@ -8412,7 +8428,7 @@ export function initMoves() {
new AttackMove(Moves.SMACK_DOWN, Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, 100, 0, 5) new AttackMove(Moves.SMACK_DOWN, Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, 100, 0, 5)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ])
.attr(HitsTagAttr, BattlerTagType.FLYING) .attr(HitsTagAttr, BattlerTagType.FLYING)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5)
@ -8462,7 +8478,7 @@ export function initMoves() {
.attr(AfterYouAttr), .attr(AfterYouAttr),
new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
.soundBased() .soundBased()
.partial(), .partial(), // No effect implemented
new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5) new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5)
.attr(ConsecutiveUseMultiBasePowerAttr, 5, false) .attr(ConsecutiveUseMultiBasePowerAttr, 5, false)
.soundBased(), .soundBased(),
@ -8504,7 +8520,8 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true)
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) 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) new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.attr(RemoveHeldItemAttr, true), .attr(RemoveHeldItemAttr, true),
@ -8572,7 +8589,8 @@ export function initMoves() {
.attr(CritOnlyAttr), .attr(CritOnlyAttr),
new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr)
.hidesTarget(), .hidesTarget()
.partial(), // Should force random switches
new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5) new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true),
new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5)
@ -8700,7 +8718,7 @@ export function initMoves() {
.ignoresVirtual(), .ignoresVirtual(),
new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6) new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6)
.attr(AddTypeAttr, Type.GHOST) .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) new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1)
.soundBased(), .soundBased(),
@ -8713,7 +8731,7 @@ export function initMoves() {
.triageMove(), .triageMove(),
new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6) new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6)
.attr(AddTypeAttr, Type.GRASS) .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) new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6)
.windMove() .windMove()
.makesContact(false) .makesContact(false)
@ -8721,7 +8739,7 @@ export function initMoves() {
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6) new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
.attr(StatusEffectAttr, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.FREEZE)
.attr(WaterSuperEffectTypeMultiplierAttr) .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) new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
@ -8845,9 +8863,9 @@ export function initMoves() {
.attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
.attr(HitsTagAttr, BattlerTagType.FLYING) .attr(HitsTagAttr, BattlerTagType.FLYING)
.attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN) .attr(HitsTagAttr, BattlerTagType.FLOATING)
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ])
.makesContact(false) .makesContact(false)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.THOUSAND_WAVES, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) new AttackMove(Moves.THOUSAND_WAVES, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6)
@ -9099,15 +9117,15 @@ export function initMoves() {
/* Unused */ /* Unused */
new AttackMove(Moves.SINISTER_ARROW_RAID, Type.GHOST, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7) new AttackMove(Moves.SINISTER_ARROW_RAID, Type.GHOST, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
.makesContact(false) .makesContact(false)
.partial() .edgeCase() // I assume it's because the user needs spirit shackle and decidueye
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7) new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7)
.attr(AlwaysHitMinimizeAttr) .attr(AlwaysHitMinimizeAttr)
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
.partial() .edgeCase() // I assume it's because it needs darkest lariat and incineroar
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) 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(), .ignoresVirtual(),
new AttackMove(Moves.GUARDIAN_OF_ALOLA, Type.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) new AttackMove(Moves.GUARDIAN_OF_ALOLA, Type.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7)
.unimplemented() .unimplemented()
@ -9116,10 +9134,10 @@ export function initMoves() {
.unimplemented() .unimplemented()
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.STOKED_SPARKSURFER, Type.ELECTRIC, MoveCategory.SPECIAL, 175, -1, 1, 100, 0, 7) 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(), .ignoresVirtual(),
new AttackMove(Moves.PULVERIZING_PANCAKE, Type.NORMAL, MoveCategory.PHYSICAL, 210, -1, 1, -1, 0, 7) 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(), .ignoresVirtual(),
new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, -1, 0, 7) 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) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true)
@ -9150,13 +9168,13 @@ export function initMoves() {
.attr(RechargeAttr), .attr(RechargeAttr),
new AttackMove(Moves.SPECTRAL_THIEF, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7) new AttackMove(Moves.SPECTRAL_THIEF, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7)
.ignoresSubstitute() .ignoresSubstitute()
.partial(), .partial(), // Does not steal stats
new AttackMove(Moves.SUNSTEEL_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7) new AttackMove(Moves.SUNSTEEL_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7)
.ignoresAbilities() .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) new AttackMove(Moves.MOONGEIST_BEAM, Type.GHOST, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
.ignoresAbilities() .ignoresAbilities()
.partial(), .edgeCase(), // Should not ignore abilities when called virtually (metronome)
new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7) new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1),
new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7)
@ -9167,7 +9185,7 @@ export function initMoves() {
.attr(FormChangeItemTypeAttr), .attr(FormChangeItemTypeAttr),
/* Unused */ /* Unused */
new AttackMove(Moves.TEN_MILLION_VOLT_THUNDERBOLT, Type.ELECTRIC, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) 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(), .ignoresVirtual(),
/* End Unused */ /* End Unused */
new AttackMove(Moves.MIND_BLOWN, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 7) new AttackMove(Moves.MIND_BLOWN, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 7)
@ -9180,7 +9198,7 @@ export function initMoves() {
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
.attr(PhotonGeyserCategoryAttr) .attr(PhotonGeyserCategoryAttr)
.ignoresAbilities() .ignoresAbilities()
.partial(), .edgeCase(), // Should not ignore abilities when called virtually (metronome)
/* Unused */ /* Unused */
new AttackMove(Moves.LIGHT_THAT_BURNS_THE_SKY, Type.PSYCHIC, MoveCategory.SPECIAL, 200, -1, 1, -1, 0, 7) new AttackMove(Moves.LIGHT_THAT_BURNS_THE_SKY, Type.PSYCHIC, MoveCategory.SPECIAL, 200, -1, 1, -1, 0, 7)
.attr(PhotonGeyserCategoryAttr) .attr(PhotonGeyserCategoryAttr)
@ -9193,7 +9211,7 @@ export function initMoves() {
.ignoresAbilities() .ignoresAbilities()
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.LETS_SNUGGLE_FOREVER, Type.FAIRY, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7) 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(), .ignoresVirtual(),
new AttackMove(Moves.SPLINTERED_STORMSHARDS, Type.ROCK, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7) new AttackMove(Moves.SPLINTERED_STORMSHARDS, Type.ROCK, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7)
.attr(ClearTerrainAttr) .attr(ClearTerrainAttr)
@ -9203,7 +9221,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true)
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.partial() .edgeCase() // I assume it needs clanging scales and Kommo-O
.ignoresVirtual(), .ignoresVirtual(),
/* End Unused */ /* End Unused */
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation
@ -9266,14 +9284,14 @@ export function initMoves() {
new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8)
.attr(JawLockAttr) .attr(JawLockAttr)
.bitingMove(), .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(EatBerryAttr)
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true)
.condition((user) => { .condition((user) => {
const userBerries = user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()); const userBerries = user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer());
return userBerries.length > 0; 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) 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(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true)
.attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false) .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false)
@ -9287,7 +9305,7 @@ export function initMoves() {
new AttackMove(Moves.DRAGON_DARTS, Type.DRAGON, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 8) new AttackMove(Moves.DRAGON_DARTS, Type.DRAGON, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 8)
.attr(MultiHitAttr, MultiHitType._2) .attr(MultiHitAttr, MultiHitType._2)
.makesContact(false) .makesContact(false)
.partial(), .partial(), // smart targetting is unimplemented
new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8) new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8)
.attr(EatBerryAttr) .attr(EatBerryAttr)
.target(MoveTarget.ALL), .target(MoveTarget.ALL),
@ -9726,7 +9744,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2),
new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9)
.makesContact(false) .makesContact(false)
.partial(), .partial(), // No effect implemented (requires Commander)
new AttackMove(Moves.JET_PUNCH, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) new AttackMove(Moves.JET_PUNCH, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9)
.punchingMove(), .punchingMove(),
new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9) new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9)
@ -9938,8 +9956,7 @@ export function initMoves() {
new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9)
.attr(FlinchAttr) .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? .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(), // Should also apply when target move priority increased by ability ex. gale wings
.partial(),
new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9)
.attr(StatusEffectAttr, StatusEffect.TOXIC) .attr(StatusEffectAttr, StatusEffect.TOXIC)
); );

View File

@ -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 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("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("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.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), 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("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 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("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.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.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 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("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("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.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.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 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("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.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), 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("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("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("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.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), 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 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("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("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.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), 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.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 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("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.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), 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.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 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("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.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.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 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("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.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.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 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("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.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.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 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("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.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), 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("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("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("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.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), 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.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 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("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.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), 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),

View File

@ -54,7 +54,7 @@ export enum BattlerTagType {
CURSED = "CURSED", CURSED = "CURSED",
CHARGED = "CHARGED", CHARGED = "CHARGED",
ROOSTED = "ROOSTED", ROOSTED = "ROOSTED",
MAGNET_RISEN = "MAGNET_RISEN", FLOATING = "FLOATING",
MINIMIZED = "MINIMIZED", MINIMIZED = "MINIMIZED",
DESTINY_BOND = "DESTINY_BOND", DESTINY_BOND = "DESTINY_BOND",
CENTER_OF_ATTENTION = "CENTER_OF_ATTENTION", CENTER_OF_ATTENTION = "CENTER_OF_ATTENTION",
@ -86,4 +86,5 @@ export enum BattlerTagType {
IMPRISON = "IMPRISON", IMPRISON = "IMPRISON",
SYRUP_BOMB = "SYRUP_BOMB", SYRUP_BOMB = "SYRUP_BOMB",
ELECTRIFIED = "ELECTRIFIED", ELECTRIFIED = "ELECTRIFIED",
TELEKINESIS = "TELEKINESIS"
} }

View File

@ -749,9 +749,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const relX = newOffset[0] - initialOffset[0]; const relX = newOffset[0] - initialOffset[0];
const relY = newOffset[1] - initialOffset[1]; const relY = newOffset[1] - initialOffset[1];
const subTag = this.getTag(SubstituteTag);
if (duration) { if (duration) {
// TODO: can this use stricter typing?
const targets: any[] = [ this ];
if (subTag?.sprite) {
targets.push(subTag.sprite);
}
this.scene.tweens.add({ this.scene.tweens.add({
targets: this, targets: targets,
x: (_target, _key, value: number) => value + relX, x: (_target, _key, value: number) => value + relX,
y: (_target, _key, value: number) => value + relY, y: (_target, _key, value: number) => value + relY,
duration: duration, duration: duration,
@ -761,6 +768,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} else { } else {
this.x += relX; this.x += relX;
this.y += relY; this.y += relY;
if (subTag?.sprite) {
subTag.sprite.x += relX;
subTag.sprite.y += relY;
}
} }
}); });
} }
@ -1488,7 +1499,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
isGrounded(): boolean { isGrounded(): boolean {
return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag));
} }
/** /**

View File

@ -8,7 +8,6 @@ export class Phase {
} }
start() { start() {
console.log(`%cStart Phase ${this.constructor.name}`, "color:green;");
if (this.scene.abilityBar.shown) { if (this.scene.abilityBar.shown) {
this.scene.abilityBar.resetAutoHideTimer(); this.scene.abilityBar.resetAutoHideTimer();
} }

View File

@ -4,7 +4,7 @@ import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr,
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
import { MoveAnim } from "#app/data/battle-anims"; import { MoveAnim } from "#app/data/battle-anims";
import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags"; import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags";
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move"; import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, OneHitKOAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move";
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
@ -404,6 +404,10 @@ export class MoveEffectPhase extends PokemonPhase {
return true; return true;
} }
if (target.getTag(BattlerTagType.TELEKINESIS) && !target.getTag(SemiInvulnerableTag) && !this.move.getMove().hasAttr(OneHitKOAttr)) {
return true;
}
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
if (semiInvulnerableTag if (semiInvulnerableTag
&& !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType) && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)

View File

@ -36,6 +36,16 @@ export class StatStageChangePhase extends PokemonPhase {
} }
start() { 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(); const pokemon = this.getPokemon();
if (!pokemon.isActive(true)) { if (!pokemon.isActive(true)) {

View 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);
});
});

View 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);
});
});

View File

@ -33,7 +33,7 @@ describe("Moves - Safeguard", () => {
.enemyLevel(5) .enemyLevel(5)
.starterSpecies(Species.DRATINI) .starterSpecies(Species.DRATINI)
.moveset([ Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH ]) .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 () => { it("protects from damaging moves with additional effects", async () => {

View File

@ -0,0 +1,124 @@
import { BattlerTagType } from "#enums/battler-tag-type";
import { allMoves } from "#app/data/move";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { MoveResult } from "#app/field/pokemon";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
describe("Moves - Telekinesis", () => {
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
.moveset([ Moves.TELEKINESIS, Moves.TACKLE, Moves.MUD_SHOT, Moves.SMACK_DOWN ])
.battleType("single")
.enemySpecies(Species.SNORLAX)
.enemyLevel(60)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset([ Moves.SPLASH ]);
});
it("Telekinesis makes the affected vulnerable to most attacking moves regardless of accuracy", async () => {
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyOpponent = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TELEKINESIS);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
await game.toNextTurn();
vi.spyOn(allMoves[Moves.TACKLE], "accuracy", "get").mockReturnValue(0);
game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.isFullHp()).toBe(false);
});
it("Telekinesis makes the affected airborne and immune to most Ground-moves", async () => {
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyOpponent = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TELEKINESIS);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
await game.toNextTurn();
vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(100);
game.move.select(Moves.MUD_SHOT);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.isFullHp()).toBe(true);
});
it("Telekinesis can still affect Pokemon that have been transformed into invalid Pokemon", async () => {
game.override.enemyMoveset(Moves.TRANSFORM);
await game.classicMode.startBattle([ Species.DIGLETT ]);
const enemyOpponent = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TELEKINESIS);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
expect(enemyOpponent.summonData.speciesForm?.speciesId).toBe(Species.DIGLETT);
});
it("Moves like Smack Down and 1000 Arrows remove all effects of Telekinesis from the target Pokemon", async () => {
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyOpponent = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TELEKINESIS);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
await game.toNextTurn();
game.move.select(Moves.SMACK_DOWN);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined();
});
it("Ingrain will remove the floating effect of Telekinesis, but not the 100% hit", async () => {
game.override.enemyMoveset([ Moves.SPLASH, Moves.INGRAIN ]);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyOpponent = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TELEKINESIS);
await game.forceEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
await game.toNextTurn();
vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(0);
game.move.select(Moves.MUD_SHOT);
await game.forceEnemyMove(Moves.INGRAIN);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.INGRAIN)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined();
expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
});
});

View File

@ -85,13 +85,13 @@ describe("Moves - Thousand Arrows", () => {
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); enemyPokemon.addTag(BattlerTagType.FLOATING, undefined, Moves.MAGNET_RISE);
game.move.select(Moves.THOUSAND_ARROWS); game.move.select(Moves.THOUSAND_ARROWS);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to(BerryPhase, false);
expect(enemyPokemon.getTag(BattlerTagType.MAGNET_RISEN)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.FLOATING)).toBeUndefined();
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
} }

View File

@ -18,6 +18,7 @@ import { Moves } from "#enums/moves";
import { ShinyRateBoosterModifier } from "#app/modifier/modifier"; import { ShinyRateBoosterModifier } from "#app/modifier/modifier";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import i18next from "i18next"; import i18next from "i18next";
import { Abilities } from "#enums/abilities";
const namespace = "mysteryEncounters/anOfferYouCantRefuse"; const namespace = "mysteryEncounters/anOfferYouCantRefuse";
/** Gyarados for Indimidate */ /** Gyarados for Indimidate */
@ -37,10 +38,11 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
beforeEach(async () => { beforeEach(async () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
scene = game.scene; scene = game.scene;
game.override.mysteryEncounterChance(100); game.override.mysteryEncounterChance(100)
game.override.startingWave(defaultWave); .startingWave(defaultWave)
game.override.startingBiome(defaultBiome); .startingBiome(defaultBiome)
game.override.disableTrainerWaves(); .disableTrainerWaves()
.ability(Abilities.INTIMIDATE); // Extortion ability
const biomeMap = new Map<Biome, MysteryEncounterType[]>([ const biomeMap = new Map<Biome, MysteryEncounterType[]>([
[ Biome.VOLCANO, [ MysteryEncounterType.MYSTERIOUS_CHALLENGERS ]], [ 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 () => { 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 ]); await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [ Species.ABRA ]);
const party = scene.getParty(); const party = scene.getParty();
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!; const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!;

View File

@ -176,10 +176,12 @@ describe("Berries Abound - Mystery Encounter", () => {
const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText"); const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText");
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); 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 config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; 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); await runMysteryEncounterToEnd(game, 2, undefined, true);
@ -198,10 +200,12 @@ describe("Berries Abound - Mystery Encounter", () => {
const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText"); const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText");
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); 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 config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; 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); await runMysteryEncounterToEnd(game, 2, undefined, true);

View File

@ -147,8 +147,8 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
expect(scene.getParty().length).toBe(initialPartySize + 1); expect(scene.getParty().length).toBe(initialPartySize + 1);
const newlyPurchasedPokemon = scene.getParty().find(p => p.name === pokemonName); const newlyPurchasedPokemon = scene.getParty()[scene.getParty().length - 1];
expect(newlyPurchasedPokemon).toBeDefined(); expect(newlyPurchasedPokemon.name).toBe(pokemonName);
expect(newlyPurchasedPokemon!.moveset.length > 0).toBeTruthy(); expect(newlyPurchasedPokemon!.moveset.length > 0).toBeTruthy();
}); });