[Ability] Serene Grace, Shield Dust, Sheer Force (P) (#246)

Co-authored-by: okimin <danielgaston6@gmail.com>
This commit is contained in:
NxKarim 2024-06-17 18:30:42 -06:00 committed by GitHub
parent 3b3b9e39af
commit c52e0ac5b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 657 additions and 137 deletions

112
src/data/ability.ts Executable file → Normal file
View File

@ -1045,6 +1045,56 @@ export class PreAttackAbAttr extends AbAttr {
} }
} }
/**
* Modifies moves additional effects with multipliers, ie. Sheer Force, Serene Grace.
* @extends AbAttr
* @see {@linkcode apply}
*/
export class MoveEffectChanceMultiplierAbAttr extends AbAttr {
private chanceMultiplier: number;
constructor(chanceMultiplier?: number) {
super(true);
this.chanceMultiplier = chanceMultiplier;
}
/**
* @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. Has to be higher than or equal to 0.
* [1]: {@linkcode Moves } Move used by the ability user.
*/
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if ((args[0] as Utils.NumberHolder).value <= 0 || (args[1] as Move).id === Moves.ORDER_UP) {
return false;
}
(args[0] as Utils.NumberHolder).value *= this.chanceMultiplier;
(args[0] as Utils.NumberHolder).value = Math.min((args[0] as Utils.NumberHolder).value, 100);
return true;
}
}
/**
* Sets incoming moves additional effect chance to zero, ignoring all effects from moves. ie. Shield Dust.
* @extends PreDefendAbAttr
* @see {@linkcode applyPreDefend}
*/
export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr {
/**
* @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance.
*/
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if ((args[0] as Utils.NumberHolder).value <= 0) {
return false;
}
(args[0] as Utils.NumberHolder).value = 0;
return true;
}
}
export class VariableMovePowerAbAttr extends PreAttackAbAttr { export class VariableMovePowerAbAttr extends PreAttackAbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder; //const power = args[0] as Utils.NumberHolder;
@ -1418,7 +1468,8 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
} }
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { /**Status inflicted by abilities post attacking are also considered additional effects.*/
if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
return attacker.trySetStatus(effect, true, pokemon); return attacker.trySetStatus(effect, true, pokemon);
} }
@ -1448,10 +1499,9 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
} }
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/
if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
return attacker.addTag(effect); return attacker.addTag(effect);
} }
@ -2402,6 +2452,35 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
} }
} }
/**
* Condition function to applied to abilities related to Sheer Force.
* Checks if last move used against target was affected by a Sheer Force user and:
* Disables: Color Change, Pickpocket, Wimp Out, Emergency Exit, Berserk, Anger Shell
* @returns {AbAttrCondition} If false disables the ability which the condition is applied to.
*/
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
return (pokemon: Pokemon) => {
if (!pokemon.turnData) {
return true;
}
const lastReceivedAttack = pokemon.turnData.attacksReceived[0];
if (!lastReceivedAttack) {
return true;
}
const lastAttacker = pokemon.getOpponents().find(p => p.id === lastReceivedAttack.sourceId);
if (!lastAttacker) {
return true;
}
/**if the last move chance is greater than or equal to cero, and the last attacker's ability is sheer force*/
const SheerForceAffected = allMoves[lastReceivedAttack.move].chance >= 0 && lastAttacker.hasAbility(Abilities.SHEER_FORCE);
return !SheerForceAffected;
};
}
function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition { function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition {
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
if (pokemon.scene.arena.weather?.isEffectSuppressed(pokemon.scene)) { if (pokemon.scene.arena.weather?.isEffectSuppressed(pokemon.scene)) {
@ -3932,7 +4011,8 @@ export function initAbilities() {
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
.ignorable(), .ignorable(),
new Ability(Abilities.COLOR_CHANGE, 3) new Ability(Abilities.COLOR_CHANGE, 3)
.attr(PostDefendTypeChangeAbAttr), .attr(PostDefendTypeChangeAbAttr)
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.IMMUNITY, 3) new Ability(Abilities.IMMUNITY, 3)
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.ignorable(), .ignorable(),
@ -3940,8 +4020,8 @@ export function initAbilities() {
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE) .attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE)
.ignorable(), .ignorable(),
new Ability(Abilities.SHIELD_DUST, 3) new Ability(Abilities.SHIELD_DUST, 3)
.ignorable() .attr(IgnoreMoveEffectsAbAttr)
.unimplemented(), .partial(),
new Ability(Abilities.OWN_TEMPO, 3) new Ability(Abilities.OWN_TEMPO, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED) .attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
.attr(IntimidateImmunityAbAttr) .attr(IntimidateImmunityAbAttr)
@ -3984,7 +4064,8 @@ export function initAbilities() {
.attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1) .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1)
.ignorable(), .ignorable(),
new Ability(Abilities.SERENE_GRACE, 3) new Ability(Abilities.SERENE_GRACE, 3)
.unimplemented(), .attr(MoveEffectChanceMultiplierAbAttr, 2)
.partial(),
new Ability(Abilities.SWIFT_SWIM, 3) new Ability(Abilities.SWIFT_SWIM, 3)
.attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
.condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)),
@ -4260,9 +4341,12 @@ export function initAbilities() {
new Ability(Abilities.BAD_DREAMS, 4) new Ability(Abilities.BAD_DREAMS, 4)
.attr(PostTurnHurtIfSleepingAbAttr), .attr(PostTurnHurtIfSleepingAbAttr),
new Ability(Abilities.PICKPOCKET, 5) new Ability(Abilities.PICKPOCKET, 5)
.attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT)), .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT))
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.SHEER_FORCE, 5) new Ability(Abilities.SHEER_FORCE, 5)
.unimplemented(), .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461/4096)
.attr(MoveEffectChanceMultiplierAbAttr, 0)
.partial(),
new Ability(Abilities.CONTRARY, 5) new Ability(Abilities.CONTRARY, 5)
.attr(StatChangeMultiplierAbAttr, -1) .attr(StatChangeMultiplierAbAttr, -1)
.ignorable(), .ignorable(),
@ -4471,8 +4555,10 @@ export function initAbilities() {
new Ability(Abilities.STAMINA, 7) new Ability(Abilities.STAMINA, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1),
new Ability(Abilities.WIMP_OUT, 7) new Ability(Abilities.WIMP_OUT, 7)
.condition(getSheerForceHitDisableAbCondition())
.unimplemented(), .unimplemented(),
new Ability(Abilities.EMERGENCY_EXIT, 7) new Ability(Abilities.EMERGENCY_EXIT, 7)
.condition(getSheerForceHitDisableAbCondition())
.unimplemented(), .unimplemented(),
new Ability(Abilities.WATER_COMPACTION, 7) new Ability(Abilities.WATER_COMPACTION, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2),
@ -4498,7 +4584,8 @@ export function initAbilities() {
new Ability(Abilities.STEELWORKER, 7) new Ability(Abilities.STEELWORKER, 7)
.attr(MoveTypePowerBoostAbAttr, Type.STEEL), .attr(MoveTypePowerBoostAbAttr, Type.STEEL),
new Ability(Abilities.BERSERK, 7) new Ability(Abilities.BERSERK, 7)
.attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [BattleStat.SPATK], 1), .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [BattleStat.SPATK], 1)
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.SLUSH_RUSH, 7) new Ability(Abilities.SLUSH_RUSH, 7)
.attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
.condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)), .condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)),
@ -4757,7 +4844,8 @@ export function initAbilities() {
.ignorable(), .ignorable(),
new Ability(Abilities.ANGER_SHELL, 9) new Ability(Abilities.ANGER_SHELL, 9)
.attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 1) .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 1)
.attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.DEF, BattleStat.SPDEF ], -1), .attr(PostDefendHpGatedStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ BattleStat.DEF, BattleStat.SPDEF ], -1)
.condition(getSheerForceHitDisableAbCondition()),
new Ability(Abilities.PURIFYING_SALT, 9) new Ability(Abilities.PURIFYING_SALT, 9)
.attr(StatusEffectImmunityAbAttr) .attr(StatusEffectImmunityAbAttr)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.GHOST, 0.5) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.GHOST, 0.5)

View File

@ -9,7 +9,7 @@ import { Type } from "./type";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { WeatherType } from "./weather"; import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr } from "./ability"; import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr } from "./ability";
import { allAbilities } from "./ability"; import { allAbilities } from "./ability";
import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier } from "../modifier/modifier"; import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier } from "../modifier/modifier";
import { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
@ -829,6 +829,22 @@ export class MoveEffectAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
return this.canApply(user, target, move, args); return this.canApply(user, target, move, args);
} }
/**
* Gets the used move's additional effect chance.
* If user's ability has MoveEffectChanceMultiplierAbAttr or IgnoreMoveEffectsAbAttr modifies the base chance.
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @param selfEffect {@linkcode Boolean} if move targets user.
* @returns Move chance value.
*/
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean): integer {
const moveChance = new Utils.NumberHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, moveChance, move, target, selfEffect);
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr,target,user,null,null, moveChance);
return moveChance.value;
}
} }
export class PreMoveMessageAttr extends MoveAttr { export class PreMoveMessageAttr extends MoveAttr {
@ -1673,7 +1689,8 @@ export class StatusEffectAttr extends MoveEffectAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const statusCheck = move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance; const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance;
if (statusCheck) { if (statusCheck) {
const pokemon = this.selfTarget ? user : target; const pokemon = this.selfTarget ? user : target;
if (pokemon.status) { if (pokemon.status) {
@ -1683,7 +1700,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
return false; return false;
} }
} }
if ((!pokemon.status || (pokemon.status.effect === this.effect && move.chance < 0)) if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect); applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect);
return true; return true;
@ -1693,7 +1710,8 @@ export class StatusEffectAttr extends MoveEffectAttr {
} }
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(move.chance * -0.1) : 0; const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(moveChance * -0.1) : 0;
} }
} }
@ -1712,7 +1730,8 @@ export class MultiStatusEffectAttr extends StatusEffectAttr {
} }
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(move.chance * -0.1) : 0; const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(moveChance * -0.1) : 0;
} }
} }
@ -2315,7 +2334,8 @@ export class StatChangeAttr extends MoveEffectAttr {
return false; return false;
} }
if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) { const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
const levels = this.getLevels(user); const levels = this.getLevels(user);
user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage)); user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage));
return true; return true;
@ -3892,18 +3912,14 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
return false; return false;
} }
const chance = this.getTagChance(user, target, move); const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
if (chance < 0 || chance === 100 || user.randSeedInt(100) < chance) { if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id); return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id);
} }
return false; return false;
} }
getTagChance(user: Pokemon, target: Pokemon, move: Move): integer {
return move.chance;
}
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return this.failOnOverlap return this.failOnOverlap
? (user, target, move) => !(this.selfTarget ? user : target).getTag(this.tagType) ? (user, target, move) => !(this.selfTarget ? user : target).getTag(this.tagType)
@ -3953,11 +3969,11 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
} }
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let chance = this.getTagChance(user, target, move); let moveChance = this.getMoveChance(user,target,move,this.selfTarget);
if (chance < 0) { if (moveChance < 0) {
chance = 100; moveChance = 100;
} }
return Math.floor(this.getTagTargetBenefitScore(user, target, move) * (chance / 100)); return Math.floor(this.getTagTargetBenefitScore(user, target, move) * (moveChance / 100));
} }
} }
@ -4221,10 +4237,14 @@ export class RemoveArenaTagsAttr extends MoveEffectAttr {
export class AddArenaTrapTagAttr extends AddArenaTagAttr { export class AddArenaTrapTagAttr extends AddArenaTagAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => { return (user, target, move) => {
const moveChance = this.getMoveChance(user,target,move,this.selfTarget);
const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
if (move.category !== MoveCategory.STATUS || !user.scene.arena.getTagOnSide(this.tagType, side)) { if (move.category !== MoveCategory.STATUS || !user.scene.arena.getTagOnSide(this.tagType, side)) {
return true; return true;
} }
}
const tag = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag; const tag = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
return tag.layers < tag.maxLayers; return tag.layers < tag.maxLayers;
}; };
@ -5617,7 +5637,7 @@ export function initMoves() {
.attr(ChargeAttr, ChargeAnim.FLY_CHARGING, "flew\nup high!", BattlerTagType.FLYING) .attr(ChargeAttr, ChargeAnim.FLY_CHARGING, "flew\nup high!", BattlerTagType.FLYING)
.condition(failOnGravityCondition) .condition(failOnGravityCondition)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.BIND, Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, 100, 0, 1) new AttackMove(Moves.BIND, Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1)
.attr(TrapAttr, BattlerTagType.BIND), .attr(TrapAttr, BattlerTagType.BIND),
new AttackMove(Moves.SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1), new AttackMove(Moves.SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1),
new AttackMove(Moves.VINE_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1), new AttackMove(Moves.VINE_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1),
@ -5650,7 +5670,7 @@ export function initMoves() {
.attr(MinimizeAccuracyAttr) .attr(MinimizeAccuracyAttr)
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS), .attr(StatusEffectAttr, StatusEffect.PARALYSIS),
new AttackMove(Moves.WRAP, Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, 100, 0, 1) new AttackMove(Moves.WRAP, Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1)
.attr(TrapAttr, BattlerTagType.WRAP), .attr(TrapAttr, BattlerTagType.WRAP),
new AttackMove(Moves.TAKE_DOWN, Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, 0, 1) new AttackMove(Moves.TAKE_DOWN, Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, 0, 1)
.attr(RecoilAttr) .attr(RecoilAttr)
@ -5675,7 +5695,7 @@ export function initMoves() {
new AttackMove(Moves.PIN_MISSILE, Type.BUG, MoveCategory.PHYSICAL, 25, 95, 20, -1, 0, 1) new AttackMove(Moves.PIN_MISSILE, Type.BUG, MoveCategory.PHYSICAL, 25, 95, 20, -1, 0, 1)
.attr(MultiHitAttr) .attr(MultiHitAttr)
.makesContact(false), .makesContact(false),
new StatusMove(Moves.LEER, Type.NORMAL, 100, 30, 100, 0, 1) new StatusMove(Moves.LEER, Type.NORMAL, 100, 30, -1, 0, 1)
.attr(StatChangeAttr, BattleStat.DEF, -1) .attr(StatChangeAttr, BattleStat.DEF, -1)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.BITE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 1) new AttackMove(Moves.BITE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 1)
@ -5784,7 +5804,7 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.DRAGON_RAGE, Type.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 1) new AttackMove(Moves.DRAGON_RAGE, Type.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 1)
.attr(FixedDamageAttr, 40), .attr(FixedDamageAttr, 40),
new AttackMove(Moves.FIRE_SPIN, Type.FIRE, MoveCategory.SPECIAL, 35, 85, 15, 100, 0, 1) new AttackMove(Moves.FIRE_SPIN, Type.FIRE, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 1)
.attr(TrapAttr, BattlerTagType.FIRE_SPIN), .attr(TrapAttr, BattlerTagType.FIRE_SPIN),
new AttackMove(Moves.THUNDER_SHOCK, Type.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) new AttackMove(Moves.THUNDER_SHOCK, Type.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS), .attr(StatusEffectAttr, StatusEffect.PARALYSIS),
@ -5900,11 +5920,11 @@ export function initMoves() {
.attr(StatusEffectAttr, StatusEffect.BURN), .attr(StatusEffectAttr, StatusEffect.BURN),
new AttackMove(Moves.WATERFALL, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 1) new AttackMove(Moves.WATERFALL, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 1)
.attr(FlinchAttr), .attr(FlinchAttr),
new AttackMove(Moves.CLAMP, Type.WATER, MoveCategory.PHYSICAL, 35, 85, 15, 100, 0, 1) new AttackMove(Moves.CLAMP, Type.WATER, MoveCategory.PHYSICAL, 35, 85, 15, -1, 0, 1)
.attr(TrapAttr, BattlerTagType.CLAMP), .attr(TrapAttr, BattlerTagType.CLAMP),
new AttackMove(Moves.SWIFT, Type.NORMAL, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 1) new AttackMove(Moves.SWIFT, Type.NORMAL, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 1)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.SKULL_BASH, Type.NORMAL, MoveCategory.PHYSICAL, 130, 100, 10, 100, 0, 1) new AttackMove(Moves.SKULL_BASH, Type.NORMAL, MoveCategory.PHYSICAL, 130, 100, 10, -1, 0, 1)
.attr(ChargeAttr, ChargeAnim.SKULL_BASH_CHARGING, "lowered\nits head!", null, true) .attr(ChargeAttr, ChargeAnim.SKULL_BASH_CHARGING, "lowered\nits head!", null, true)
.attr(StatChangeAttr, BattleStat.DEF, 1, true) .attr(StatChangeAttr, BattleStat.DEF, 1, true)
.ignoresVirtual(), .ignoresVirtual(),
@ -6253,7 +6273,7 @@ export function initMoves() {
.attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, "foresaw\nan attack!"), .attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, "foresaw\nan attack!"),
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(StatChangeAttr, BattleStat.DEF, -1), .attr(StatChangeAttr, BattleStat.DEF, -1),
new AttackMove(Moves.WHIRLPOOL, Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, 100, 0, 2) new AttackMove(Moves.WHIRLPOOL, Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2)
.attr(TrapAttr, BattlerTagType.WHIRLPOOL) .attr(TrapAttr, BattlerTagType.WHIRLPOOL)
.attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true),
new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2) new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2)
@ -6329,7 +6349,7 @@ export function initMoves() {
.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),
new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, 100, 0, 3) new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true), .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.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)
.unimplemented(), .unimplemented(),
@ -6431,7 +6451,7 @@ export function initMoves() {
.slicingMove() .slicingMove()
.windMove() .windMove()
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.OVERHEAT, Type.FIRE, MoveCategory.SPECIAL, 130, 90, 5, 100, 0, 3) new AttackMove(Moves.OVERHEAT, Type.FIRE, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 3)
.attr(StatChangeAttr, BattleStat.SPATK, -2, true) .attr(StatChangeAttr, BattleStat.SPATK, -2, true)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE), .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE),
new StatusMove(Moves.ODOR_SLEUTH, Type.NORMAL, -1, 40, -1, 0, 3) new StatusMove(Moves.ODOR_SLEUTH, Type.NORMAL, -1, 40, -1, 0, 3)
@ -6464,7 +6484,7 @@ export function initMoves() {
new AttackMove(Moves.SKY_UPPERCUT, Type.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, 0, 3) new AttackMove(Moves.SKY_UPPERCUT, Type.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, 0, 3)
.attr(HitsTagAttr, BattlerTagType.FLYING) .attr(HitsTagAttr, BattlerTagType.FLYING)
.punchingMove(), .punchingMove(),
new AttackMove(Moves.SAND_TOMB, Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, 100, 0, 3) new AttackMove(Moves.SAND_TOMB, Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, -1, 0, 3)
.attr(TrapAttr, BattlerTagType.SAND_TOMB) .attr(TrapAttr, BattlerTagType.SAND_TOMB)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.SHEER_COLD, Type.ICE, MoveCategory.SPECIAL, 200, 20, 5, -1, 0, 3) new AttackMove(Moves.SHEER_COLD, Type.ICE, MoveCategory.SPECIAL, 200, 20, 5, -1, 0, 3)
@ -6534,7 +6554,7 @@ export function initMoves() {
.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)
.attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, "chose\nDoom Desire as its destiny!"), .attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, "chose\nDoom Desire as its destiny!"),
new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, 100, 0, 3) new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3)
.attr(StatChangeAttr, BattleStat.SPATK, -2, true), .attr(StatChangeAttr, BattleStat.SPATK, -2, true),
new SelfStatusMove(Moves.ROOST, Type.FLYING, -1, 5, -1, 0, 4) new SelfStatusMove(Moves.ROOST, Type.FLYING, -1, 5, -1, 0, 4)
.attr(HealAttr, 0.5) .attr(HealAttr, 0.5)
@ -6548,7 +6568,7 @@ export function initMoves() {
new AttackMove(Moves.WAKE_UP_SLAP, Type.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 4) new AttackMove(Moves.WAKE_UP_SLAP, Type.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 4)
.attr(MovePowerMultiplierAttr, (user, target, move) => targetSleptOrComatoseCondition(user, target, move) ? 2 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => targetSleptOrComatoseCondition(user, target, move) ? 2 : 1)
.attr(HealStatusEffectAttr, false, StatusEffect.SLEEP), .attr(HealStatusEffectAttr, false, StatusEffect.SLEEP),
new AttackMove(Moves.HAMMER_ARM, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 90, 10, 100, 0, 4) new AttackMove(Moves.HAMMER_ARM, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 4)
.attr(StatChangeAttr, BattleStat.SPD, -1, true) .attr(StatChangeAttr, BattleStat.SPD, -1, true)
.punchingMove(), .punchingMove(),
new AttackMove(Moves.GYRO_BALL, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) new AttackMove(Moves.GYRO_BALL, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
@ -6582,7 +6602,7 @@ export function initMoves() {
.target(MoveTarget.ATTACKER), .target(MoveTarget.ATTACKER),
new AttackMove(Moves.U_TURN, Type.BUG, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) new AttackMove(Moves.U_TURN, Type.BUG, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4)
.attr(ForceSwitchOutAttr, true, false), .attr(ForceSwitchOutAttr, true, false),
new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, 100, 0, 4) new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4)
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true),
new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1), .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1),
@ -6626,9 +6646,9 @@ export function initMoves() {
new SelfStatusMove(Moves.COPYCAT, Type.NORMAL, -1, 20, -1, 0, 4) new SelfStatusMove(Moves.COPYCAT, Type.NORMAL, -1, 20, -1, 0, 4)
.attr(CopyMoveAttr) .attr(CopyMoveAttr)
.ignoresVirtual(), .ignoresVirtual(),
new StatusMove(Moves.POWER_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 4) new StatusMove(Moves.POWER_SWAP, Type.PSYCHIC, -1, 10, 100, 0, 4)
.unimplemented(), .unimplemented(),
new StatusMove(Moves.GUARD_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 4) new StatusMove(Moves.GUARD_SWAP, Type.PSYCHIC, -1, 10, 100, 0, 4)
.unimplemented(), .unimplemented(),
new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
.makesContact(true) .makesContact(true)
@ -6756,7 +6776,7 @@ export function initMoves() {
.attr(AddArenaTagAttr, ArenaTagType.TRICK_ROOM, 5) .attr(AddArenaTagAttr, ArenaTagType.TRICK_ROOM, 5)
.ignoresProtect() .ignoresProtect()
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.DRACO_METEOR, Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, 100, 0, 4) new AttackMove(Moves.DRACO_METEOR, Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4)
.attr(StatChangeAttr, BattleStat.SPATK, -2, true), .attr(StatChangeAttr, BattleStat.SPATK, -2, true),
new AttackMove(Moves.DISCHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4) new AttackMove(Moves.DISCHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS) .attr(StatusEffectAttr, StatusEffect.PARALYSIS)
@ -6764,7 +6784,7 @@ export function initMoves() {
new AttackMove(Moves.LAVA_PLUME, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4) new AttackMove(Moves.LAVA_PLUME, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4)
.attr(StatusEffectAttr, StatusEffect.BURN) .attr(StatusEffectAttr, StatusEffect.BURN)
.target(MoveTarget.ALL_NEAR_OTHERS), .target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.LEAF_STORM, Type.GRASS, MoveCategory.SPECIAL, 130, 90, 5, 100, 0, 4) new AttackMove(Moves.LEAF_STORM, Type.GRASS, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4)
.attr(StatChangeAttr, BattleStat.SPATK, -2, true), .attr(StatChangeAttr, BattleStat.SPATK, -2, true),
new AttackMove(Moves.POWER_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 4), new AttackMove(Moves.POWER_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 4),
new AttackMove(Moves.ROCK_WRECKER, Type.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) new AttackMove(Moves.ROCK_WRECKER, Type.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4)
@ -6834,7 +6854,7 @@ export function initMoves() {
.unimplemented(), .unimplemented(),
new AttackMove(Moves.CRUSH_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) new AttackMove(Moves.CRUSH_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
.attr(OpponentHighHpPowerAttr), .attr(OpponentHighHpPowerAttr),
new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, 100, 0, 4) new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4)
.attr(TrapAttr, BattlerTagType.MAGMA_STORM), .attr(TrapAttr, BattlerTagType.MAGMA_STORM),
new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4) new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4)
.attr(StatusEffectAttr, StatusEffect.SLEEP) .attr(StatusEffectAttr, StatusEffect.SLEEP)
@ -7094,7 +7114,7 @@ export function initMoves() {
new AttackMove(Moves.ICICLE_CRASH, Type.ICE, MoveCategory.PHYSICAL, 85, 90, 10, 30, 0, 5) new AttackMove(Moves.ICICLE_CRASH, Type.ICE, MoveCategory.PHYSICAL, 85, 90, 10, 30, 0, 5)
.attr(FlinchAttr) .attr(FlinchAttr)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.V_CREATE, Type.FIRE, MoveCategory.PHYSICAL, 180, 95, 5, 100, 0, 5) new AttackMove(Moves.V_CREATE, Type.FIRE, MoveCategory.PHYSICAL, 180, 95, 5, -1, 0, 5)
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF, BattleStat.SPD ], -1, true), .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF, BattleStat.SPD ], -1, true),
new AttackMove(Moves.FUSION_FLARE, Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 5) new AttackMove(Moves.FUSION_FLARE, Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 5)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
@ -7113,7 +7133,7 @@ export function initMoves() {
.condition(new FirstMoveCondition()), .condition(new FirstMoveCondition()),
new AttackMove(Moves.BELCH, Type.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6) new AttackMove(Moves.BELCH, Type.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6)
.condition((user, target, move) => user.battleData.berriesEaten.length > 0), .condition((user, target, move) => user.battleData.berriesEaten.length > 0),
new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, 100, 0, 6) new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, -1, 0, 6)
.target(MoveTarget.ALL) .target(MoveTarget.ALL)
.condition((user,target,move) => { .condition((user,target,move) => {
// If any fielded pokémon is grass-type and grounded. // If any fielded pokémon is grass-type and grounded.
@ -7132,7 +7152,7 @@ export function initMoves() {
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(), .partial(),
new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, 100, 0, 6) new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1)
.soundBased(), .soundBased(),
new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6) new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6)
@ -7155,7 +7175,7 @@ export function initMoves() {
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),
new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, 100, 0, 6) new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, -1, 0, 6)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY)
.attr(ForceSwitchOutAttr, true, false) .attr(ForceSwitchOutAttr, true, false)
.soundBased(), .soundBased(),
@ -7168,7 +7188,7 @@ export function initMoves() {
new StatusMove(Moves.CRAFTY_SHIELD, Type.FAIRY, -1, 10, -1, 3, 6) new StatusMove(Moves.CRAFTY_SHIELD, Type.FAIRY, -1, 10, -1, 3, 6)
.target(MoveTarget.USER_SIDE) .target(MoveTarget.USER_SIDE)
.attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true), .attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true),
new StatusMove(Moves.FLOWER_SHIELD, Type.FAIRY, -1, 10, 100, 0, 6) new StatusMove(Moves.FLOWER_SHIELD, Type.FAIRY, -1, 10, -1, 0, 6)
.target(MoveTarget.ALL) .target(MoveTarget.ALL)
.unimplemented(), .unimplemented(),
new StatusMove(Moves.GRASSY_TERRAIN, Type.GRASS, -1, 10, -1, 0, 6) new StatusMove(Moves.GRASSY_TERRAIN, Type.GRASS, -1, 10, -1, 0, 6)
@ -7193,9 +7213,9 @@ export function initMoves() {
.unimplemented(), .unimplemented(),
new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6) new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6)
.attr(ProtectAttr, BattlerTagType.KINGS_SHIELD), .attr(ProtectAttr, BattlerTagType.KINGS_SHIELD),
new StatusMove(Moves.PLAY_NICE, Type.NORMAL, -1, 20, 100, 0, 6) new StatusMove(Moves.PLAY_NICE, Type.NORMAL, -1, 20, -1, 0, 6)
.attr(StatChangeAttr, BattleStat.ATK, -1), .attr(StatChangeAttr, BattleStat.ATK, -1),
new StatusMove(Moves.CONFIDE, Type.NORMAL, -1, 20, 100, 0, 6) new StatusMove(Moves.CONFIDE, Type.NORMAL, -1, 20, -1, 0, 6)
.attr(StatChangeAttr, BattleStat.SPATK, -1) .attr(StatChangeAttr, BattleStat.SPATK, -1)
.soundBased(), .soundBased(),
new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6) new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6)
@ -7221,7 +7241,7 @@ export function initMoves() {
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY),
new StatusMove(Moves.EERIE_IMPULSE, Type.ELECTRIC, 100, 15, -1, 0, 6) new StatusMove(Moves.EERIE_IMPULSE, Type.ELECTRIC, 100, 15, -1, 0, 6)
.attr(StatChangeAttr, BattleStat.SPATK, -2), .attr(StatChangeAttr, BattleStat.SPATK, -2),
new StatusMove(Moves.VENOM_DRENCH, Type.POISON, 100, 20, 100, 0, 6) new StatusMove(Moves.VENOM_DRENCH, Type.POISON, 100, 20, -1, 0, 6)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], -1, false, (user, target, move) => target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], -1, false, (user, target, move) => target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new StatusMove(Moves.POWDER, Type.BUG, 100, 20, -1, 1, 6) new StatusMove(Moves.POWDER, Type.BUG, 100, 20, -1, 1, 6)
@ -7252,7 +7272,7 @@ export function initMoves() {
.attr(StatusEffectAttr, StatusEffect.PARALYSIS), .attr(StatusEffectAttr, StatusEffect.PARALYSIS),
new AttackMove(Moves.HOLD_BACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 6) new AttackMove(Moves.HOLD_BACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 6)
.attr(SurviveDamageAttr), .attr(SurviveDamageAttr),
new AttackMove(Moves.INFESTATION, Type.BUG, MoveCategory.SPECIAL, 20, 100, 20, 100, 0, 6) new AttackMove(Moves.INFESTATION, Type.BUG, MoveCategory.SPECIAL, 20, 100, 20, -1, 0, 6)
.makesContact() .makesContact()
.attr(TrapAttr, BattlerTagType.INFESTATION), .attr(TrapAttr, BattlerTagType.INFESTATION),
new AttackMove(Moves.POWER_UP_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 20, 100, 0, 6) new AttackMove(Moves.POWER_UP_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 20, 100, 0, 6)
@ -7261,7 +7281,7 @@ export function initMoves() {
new AttackMove(Moves.OBLIVION_WING, Type.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) new AttackMove(Moves.OBLIVION_WING, Type.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6)
.attr(HitHealAttr, 0.75) .attr(HitHealAttr, 0.75)
.triageMove(), .triageMove(),
new AttackMove(Moves.THOUSAND_ARROWS, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, 100, 0, 6) new AttackMove(Moves.THOUSAND_ARROWS, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6)
.attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr)
.attr(HitsTagAttr, BattlerTagType.FLYING, false) .attr(HitsTagAttr, BattlerTagType.FLYING, false)
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
@ -7284,9 +7304,9 @@ export function initMoves() {
new AttackMove(Moves.PRECIPICE_BLADES, Type.GROUND, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 6) new AttackMove(Moves.PRECIPICE_BLADES, Type.GROUND, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 6)
.makesContact(false) .makesContact(false)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.DRAGON_ASCENT, Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 5, 100, 0, 6) new AttackMove(Moves.DRAGON_ASCENT, Type.FLYING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 6)
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true),
new AttackMove(Moves.HYPERSPACE_FURY, Type.DARK, MoveCategory.PHYSICAL, 100, -1, 5, 100, 0, 6) new AttackMove(Moves.HYPERSPACE_FURY, Type.DARK, MoveCategory.PHYSICAL, 100, -1, 5, -1, 0, 6)
.attr(StatChangeAttr, BattleStat.DEF, -1, true) .attr(StatChangeAttr, BattleStat.DEF, -1, true)
.makesContact(false) .makesContact(false)
.ignoresProtect(), .ignoresProtect(),
@ -7410,23 +7430,23 @@ export function initMoves() {
.condition(new FirstMoveCondition()), .condition(new FirstMoveCondition()),
new SelfStatusMove(Moves.BANEFUL_BUNKER, Type.POISON, -1, 10, -1, 4, 7) new SelfStatusMove(Moves.BANEFUL_BUNKER, Type.POISON, -1, 10, -1, 4, 7)
.attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER), .attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER),
new AttackMove(Moves.SPIRIT_SHACKLE, Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 7) new AttackMove(Moves.SPIRIT_SHACKLE, Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 7)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.DARKEST_LARIAT, Type.DARK, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) new AttackMove(Moves.DARKEST_LARIAT, Type.DARK, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7)
.attr(IgnoreOpponentStatChangesAttr), .attr(IgnoreOpponentStatChangesAttr),
new AttackMove(Moves.SPARKLING_ARIA, Type.WATER, MoveCategory.SPECIAL, 90, 100, 10, -1, 0, 7) new AttackMove(Moves.SPARKLING_ARIA, Type.WATER, MoveCategory.SPECIAL, 90, 100, 10, 100, 0, 7)
.attr(HealStatusEffectAttr, false, StatusEffect.BURN) .attr(HealStatusEffectAttr, false, StatusEffect.BURN)
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_OTHERS), .target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.ICE_HAMMER, Type.ICE, MoveCategory.PHYSICAL, 100, 90, 10, 100, 0, 7) new AttackMove(Moves.ICE_HAMMER, Type.ICE, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 7)
.attr(StatChangeAttr, BattleStat.SPD, -1, true) .attr(StatChangeAttr, BattleStat.SPD, -1, true)
.punchingMove(), .punchingMove(),
new StatusMove(Moves.FLORAL_HEALING, Type.FAIRY, -1, 10, -1, 0, 7) new StatusMove(Moves.FLORAL_HEALING, Type.FAIRY, -1, 10, -1, 0, 7)
.attr(BoostHealAttr, 0.5, 2/3, true, false, (user, target, move) => user.scene.arena.terrain?.terrainType === TerrainType.GRASSY) .attr(BoostHealAttr, 0.5, 2/3, true, false, (user, target, move) => user.scene.arena.terrain?.terrainType === TerrainType.GRASSY)
.triageMove(), .triageMove(),
new AttackMove(Moves.HIGH_HORSEPOWER, Type.GROUND, MoveCategory.PHYSICAL, 95, 95, 10, -1, 0, 7), new AttackMove(Moves.HIGH_HORSEPOWER, Type.GROUND, MoveCategory.PHYSICAL, 95, 95, 10, -1, 0, 7),
new StatusMove(Moves.STRENGTH_SAP, Type.GRASS, 100, 10, 100, 0, 7) new StatusMove(Moves.STRENGTH_SAP, Type.GRASS, 100, 10, -1, 0, 7)
.attr(HitHealAttr, null, Stat.ATK) .attr(HitHealAttr, null, Stat.ATK)
.attr(StatChangeAttr, BattleStat.ATK, -1) .attr(StatChangeAttr, BattleStat.ATK, -1)
.condition((user, target, move) => target.summonData.battleStats[BattleStat.ATK] > -6) .condition((user, target, move) => target.summonData.battleStats[BattleStat.ATK] > -6)
@ -7439,7 +7459,7 @@ export function initMoves() {
.makesContact(false), .makesContact(false),
new StatusMove(Moves.SPOTLIGHT, Type.NORMAL, -1, 15, -1, 3, 7) new StatusMove(Moves.SPOTLIGHT, Type.NORMAL, -1, 15, -1, 3, 7)
.attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false), .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false),
new StatusMove(Moves.TOXIC_THREAD, Type.POISON, 100, 20, 100, 0, 7) new StatusMove(Moves.TOXIC_THREAD, Type.POISON, 100, 20, -1, 0, 7)
.attr(StatusEffectAttr, StatusEffect.POISON) .attr(StatusEffectAttr, StatusEffect.POISON)
.attr(StatChangeAttr, BattleStat.SPD, -1), .attr(StatChangeAttr, BattleStat.SPD, -1),
new SelfStatusMove(Moves.LASER_FOCUS, Type.NORMAL, -1, 30, -1, 0, 7) new SelfStatusMove(Moves.LASER_FOCUS, Type.NORMAL, -1, 30, -1, 0, 7)
@ -7454,7 +7474,7 @@ export function initMoves() {
.attr(StatusCategoryOnAllyAttr) .attr(StatusCategoryOnAllyAttr)
.attr(HealOnAllyAttr, 0.5, true, false) .attr(HealOnAllyAttr, 0.5, true, false)
.ballBombMove(), .ballBombMove(),
new AttackMove(Moves.ANCHOR_SHOT, Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, -1, 0, 7) new AttackMove(Moves.ANCHOR_SHOT, Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, 100, 0, 7)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1),
new StatusMove(Moves.PSYCHIC_TERRAIN, Type.PSYCHIC, -1, 10, -1, 0, 7) new StatusMove(Moves.PSYCHIC_TERRAIN, Type.PSYCHIC, -1, 10, -1, 0, 7)
.attr(TerrainChangeAttr, TerrainType.PSYCHIC) .attr(TerrainChangeAttr, TerrainType.PSYCHIC)
@ -7498,7 +7518,7 @@ export function initMoves() {
.ballBombMove() .ballBombMove()
.makesContact(false) .makesContact(false)
.partial(), .partial(),
new AttackMove(Moves.CLANGING_SCALES, Type.DRAGON, MoveCategory.SPECIAL, 110, 100, 5, 100, 0, 7) new AttackMove(Moves.CLANGING_SCALES, Type.DRAGON, MoveCategory.SPECIAL, 110, 100, 5, -1, 0, 7)
.attr(StatChangeAttr, BattleStat.DEF, -1, true) .attr(StatChangeAttr, BattleStat.DEF, -1, true)
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
@ -7535,14 +7555,14 @@ export function initMoves() {
new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, 100, 0, 7) new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, 100, 0, 7)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.GENESIS_SUPERNOVA, Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, -1, 0, 7) new AttackMove(Moves.GENESIS_SUPERNOVA, Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7)
.attr(TerrainChangeAttr, TerrainType.PSYCHIC) .attr(TerrainChangeAttr, TerrainType.PSYCHIC)
.ignoresVirtual(), .ignoresVirtual(),
/* End Unused */ /* End Unused */
new AttackMove(Moves.SHELL_TRAP, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, -3, 7) new AttackMove(Moves.SHELL_TRAP, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, -3, 7)
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.partial(), .partial(),
new AttackMove(Moves.FLEUR_CANNON, Type.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, 100, 0, 7) new AttackMove(Moves.FLEUR_CANNON, Type.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 7)
.attr(StatChangeAttr, BattleStat.SPATK, -2, true), .attr(StatChangeAttr, BattleStat.SPATK, -2, true),
new AttackMove(Moves.PSYCHIC_FANGS, Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) new AttackMove(Moves.PSYCHIC_FANGS, Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7)
.bitingMove() .bitingMove()
@ -7565,7 +7585,7 @@ export function initMoves() {
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(), .partial(),
new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, 100, 0, 7) new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1), .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.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)
.attr(FlinchAttr), .attr(FlinchAttr),
@ -7675,7 +7695,7 @@ export function initMoves() {
new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, 100, 0, 8) new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, 100, 0, 8)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, true, 1), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, true, 1),
new StatusMove(Moves.TAR_SHOT, Type.ROCK, 100, 15, 100, 0, 8) new StatusMove(Moves.TAR_SHOT, Type.ROCK, 100, 15, -1, 0, 8)
.attr(StatChangeAttr, BattleStat.SPD, -1) .attr(StatChangeAttr, BattleStat.SPD, -1)
.partial(), .partial(),
new StatusMove(Moves.MAGIC_POWDER, Type.PSYCHIC, 100, 20, -1, 0, 8) new StatusMove(Moves.MAGIC_POWDER, Type.PSYCHIC, 100, 20, -1, 0, 8)
@ -7777,12 +7797,12 @@ export function initMoves() {
.danceMove(), .danceMove(),
new AttackMove(Moves.BODY_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.BODY_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8)
.attr(DefAtkAttr), .attr(DefAtkAttr),
new StatusMove(Moves.DECORATE, Type.FAIRY, -1, 15, 100, 0, 8) new StatusMove(Moves.DECORATE, Type.FAIRY, -1, 15, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 2), .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 2),
new AttackMove(Moves.DRUM_BEATING, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) new AttackMove(Moves.DRUM_BEATING, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8)
.attr(StatChangeAttr, BattleStat.SPD, -1) .attr(StatChangeAttr, BattleStat.SPD, -1)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.SNAP_TRAP, Type.GRASS, MoveCategory.PHYSICAL, 35, 100, 15, 100, 0, 8) new AttackMove(Moves.SNAP_TRAP, Type.GRASS, MoveCategory.PHYSICAL, 35, 100, 15, -1, 0, 8)
.attr(TrapAttr, BattlerTagType.SNAP_TRAP), .attr(TrapAttr, BattlerTagType.SNAP_TRAP),
new AttackMove(Moves.PYRO_BALL, Type.FIRE, MoveCategory.PHYSICAL, 120, 90, 5, 10, 0, 8) new AttackMove(Moves.PYRO_BALL, Type.FIRE, MoveCategory.PHYSICAL, 120, 90, 5, 10, 0, 8)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
@ -7834,7 +7854,7 @@ export function initMoves() {
new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8) new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8)
.attr(ClearTerrainAttr) .attr(ClearTerrainAttr)
.condition((user, target, move) => !!user.scene.arena.terrain), .condition((user, target, move) => !!user.scene.arena.terrain),
new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, 100, 0, 8) new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8)
//.attr(StatChangeAttr, BattleStat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit //.attr(StatChangeAttr, BattleStat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit
//.attr(StatChangeAttr, BattleStat.DEF, -1, true) //.attr(StatChangeAttr, BattleStat.DEF, -1, true)
.attr(MultiHitAttr) .attr(MultiHitAttr)
@ -7875,7 +7895,7 @@ export function initMoves() {
new StatusMove(Moves.CORROSIVE_GAS, Type.POISON, 100, 40, -1, 0, 8) new StatusMove(Moves.CORROSIVE_GAS, Type.POISON, 100, 40, -1, 0, 8)
.target(MoveTarget.ALL_NEAR_OTHERS) .target(MoveTarget.ALL_NEAR_OTHERS)
.unimplemented(), .unimplemented(),
new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, 100, 0, 8) new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1)
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY),
new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8) new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8)
@ -7901,7 +7921,7 @@ export function initMoves() {
.attr(MultiHitAttr, MultiHitType._3) .attr(MultiHitAttr, MultiHitType._3)
.attr(CritOnlyAttr) .attr(CritOnlyAttr)
.punchingMove(), .punchingMove(),
new AttackMove(Moves.THUNDER_CAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 90, 15, 100, 0, 8) new AttackMove(Moves.THUNDER_CAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 80, 90, 15, -1, 0, 8)
.attr(TrapAttr, BattlerTagType.THUNDER_CAGE), .attr(TrapAttr, BattlerTagType.THUNDER_CAGE),
new AttackMove(Moves.DRAGON_ENERGY, Type.DRAGON, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 8) new AttackMove(Moves.DRAGON_ENERGY, Type.DRAGON, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 8)
.attr(HpPowerAttr) .attr(HpPowerAttr)
@ -7950,7 +7970,7 @@ export function initMoves() {
new SelfStatusMove(Moves.VICTORY_DANCE, Type.FIGHTING, -1, 10, 100, 0, 8) new SelfStatusMove(Moves.VICTORY_DANCE, Type.FIGHTING, -1, 10, 100, 0, 8)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPD ], 1, true) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPD ], 1, true)
.danceMove(), .danceMove(),
new AttackMove(Moves.HEADLONG_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 120, 100, 5, 100, 0, 8) new AttackMove(Moves.HEADLONG_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true) .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true)
.punchingMove(), .punchingMove(),
new AttackMove(Moves.BARB_BARRAGE, Type.POISON, MoveCategory.PHYSICAL, 60, 100, 10, 50, 0, 8) new AttackMove(Moves.BARB_BARRAGE, Type.POISON, MoveCategory.PHYSICAL, 60, 100, 10, 50, 0, 8)
@ -8113,15 +8133,15 @@ export function initMoves() {
.makesContact(false), .makesContact(false),
new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.SPDEF, -2), .attr(StatChangeAttr, BattleStat.SPDEF, -2),
new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 9) new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9)
.makesContact(false) .makesContact(false)
.partial(), .partial(),
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, 100, 0, 9) new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9)
.attr(StatChangeAttr, BattleStat.ATK, 2) .attr(StatChangeAttr, BattleStat.ATK, 2)
.attr(StatChangeAttr, BattleStat.DEF, -2), .attr(StatChangeAttr, BattleStat.DEF, -2),
new AttackMove(Moves.SPIN_OUT, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, 100, 0, 9) new AttackMove(Moves.SPIN_OUT, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
.attr(StatChangeAttr, BattleStat.SPD, -2, true), .attr(StatChangeAttr, BattleStat.SPD, -2, true),
new AttackMove(Moves.POPULATION_BOMB, Type.NORMAL, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 9) new AttackMove(Moves.POPULATION_BOMB, Type.NORMAL, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 9)
.attr(MultiHitAttr, MultiHitType._10) .attr(MultiHitAttr, MultiHitType._10)
@ -8135,7 +8155,7 @@ export function initMoves() {
.triageMove() .triageMove()
.attr(RevivalBlessingAttr) .attr(RevivalBlessingAttr)
.target(MoveTarget.USER), .target(MoveTarget.USER),
new AttackMove(Moves.SALT_CURE, Type.ROCK, MoveCategory.PHYSICAL, 40, 100, 15, -1, 0, 9) new AttackMove(Moves.SALT_CURE, Type.ROCK, MoveCategory.PHYSICAL, 40, 100, 15, 100, 0, 9)
.attr(AddBattlerTagAttr, BattlerTagType.SALT_CURED) .attr(AddBattlerTagAttr, BattlerTagType.SALT_CURED)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.TRIPLE_DIVE, Type.WATER, MoveCategory.PHYSICAL, 30, 95, 10, -1, 0, 9) new AttackMove(Moves.TRIPLE_DIVE, Type.WATER, MoveCategory.PHYSICAL, 30, 95, 10, -1, 0, 9)
@ -8209,7 +8229,7 @@ export function initMoves() {
.attr(StatChangeAttr, BattleStat.SPD, -1), .attr(StatChangeAttr, BattleStat.SPD, -1),
new AttackMove(Moves.TRAILBLAZE, Type.GRASS, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) new AttackMove(Moves.TRAILBLAZE, Type.GRASS, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.SPD, 1, true), .attr(StatChangeAttr, BattleStat.SPD, 1, true),
new AttackMove(Moves.CHILLING_WATER, Type.WATER, MoveCategory.SPECIAL, 50, 100, 20, -1, 0, 9) new AttackMove(Moves.CHILLING_WATER, Type.WATER, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.ATK, -1), .attr(StatChangeAttr, BattleStat.ATK, -1),
new AttackMove(Moves.HYPER_DRILL, Type.NORMAL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) new AttackMove(Moves.HYPER_DRILL, Type.NORMAL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
.ignoresProtect(), .ignoresProtect(),
@ -8302,7 +8322,7 @@ export function initMoves() {
.slicingMove(), .slicingMove(),
new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
.attr(OpponentHighHpPowerAttr), .attr(OpponentHighHpPowerAttr),
new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, 100, 0, 9) new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9)
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true) .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true)
.target(MoveTarget.NEAR_ALLY) .target(MoveTarget.NEAR_ALLY)
.partial(), .partial(),

View File

@ -24,6 +24,9 @@ import * as Overrides from "../overrides";
import { ModifierType, modifierTypes } from "./modifier-type"; import { ModifierType, modifierTypes } from "./modifier-type";
import { Command } from "#app/ui/command-ui-handler.js"; import { Command } from "#app/ui/command-ui-handler.js";
import { allMoves } from "#app/data/move.js";
import { Abilities } from "#app/enums/abilities.js";
export type ModifierPredicate = (modifier: Modifier) => boolean; export type ModifierPredicate = (modifier: Modifier) => boolean;
const iconOverflowIndex = 24; const iconOverflowIndex = 24;
@ -520,6 +523,18 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
return 1; return 1;
} }
//Applies to items with chance of activating secondary effects ie Kings Rock
getSecondaryChanceMultiplier(pokemon: Pokemon): integer {
const sheerForceAffected = allMoves[pokemon.getLastXMoves(0)[0].move].chance >= 0 && pokemon.hasAbility(Abilities.SHEER_FORCE);
if (sheerForceAffected) {
return 0;
} else if (pokemon.hasAbility(Abilities.SERENE_GRACE)) {
return 2;
}
return 1;
}
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): integer { getMaxStackCount(scene: BattleScene, forThreshold?: boolean): integer {
const pokemon = this.getPokemon(scene); const pokemon = this.getPokemon(scene);
if (!pokemon) { if (!pokemon) {
@ -831,7 +846,7 @@ export class FlinchChanceModifier extends PokemonHeldItemModifier {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
const flinched = args[1] as Utils.BooleanHolder; const flinched = args[1] as Utils.BooleanHolder;
if (!flinched.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!flinched.value && pokemon.randSeedInt(10) < (this.getStackCount() * this.getSecondaryChanceMultiplier(pokemon))) {
flinched.value = true; flinched.value = true;
return true; return true;
} }

View File

@ -26,7 +26,7 @@ import { Gender } from "./data/gender";
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
import { TempBattleStat } from "./data/temp-battle-stat"; import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, WonderSkinAbAttr, applyPreDefendAbAttrs } from "./data/ability"; import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, WonderSkinAbAttr, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables"; import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena"; import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -2937,24 +2937,24 @@ export class MoveEffectPhase extends PokemonPhase {
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
&& attr.selfTarget && (!attr.firstHitOnly || firstHit), user, target, move)).then(() => { && attr.selfTarget && (!attr.firstHitOnly || firstHit), user, target, move)).then(() => {
if (hitResult !== HitResult.NO_EFFECT) { if (hitResult !== HitResult.NO_EFFECT) {
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
&& !attr.selfTarget && (!attr.firstHitOnly || firstHit), user, target, move).then(() => { && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove()).then(() => {
if (hitResult < HitResult.NO_EFFECT) { if (hitResult < HitResult.NO_EFFECT && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) {
const flinched = new Utils.BooleanHolder(false); const flinched = new Utils.BooleanHolder(false);
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
if (flinched.value) { if (flinched.value) {
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id); target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
} }
} }
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit), Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit),
user, target, move).then(() => { user, target, this.move.getMove()).then(() => {
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, move, hitResult).then(() => { return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => {
if (!user.isPlayer() && move instanceof AttackMove) { if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target); user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
} }
})).then(() => { })).then(() => {
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, move, hitResult).then(() => { applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => {
if (move instanceof AttackMove) { if (this.move.getMove() instanceof AttackMove) {
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex()); this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
} }
resolve(); resolve();

View File

@ -0,0 +1,110 @@
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import {Abilities} from "#enums/abilities";
import {applyAbAttrs ,MoveEffectChanceMultiplierAbAttr} from "#app/data/ability";
import {Species} from "#enums/species";
import {
CommandPhase,
MoveEffectPhase,
} from "#app/phases";
import {Mode} from "#app/ui/ui";
import {Stat} from "#app/data/pokemon-stat";
import {Moves} from "#enums/moves";
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
import {Command} from "#app/ui/command-ui-handler";
import * as Utils from "#app/utils";
describe("Abilities - Serene Grace", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
const movesToUse = [Moves.AIR_SLASH, Moves.TACKLE];
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ONIX);
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100);
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(movesToUse);
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
});
it("Move chance without Serene Grace", async() => {
const moveToUse = Moves.AIR_SLASH;
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Check chance of Air Slash without Serene Grace
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.AIR_SLASH);
const chance = new Utils.IntegerHolder(move.chance);
console.log(move.chance + " Their ability is " + phase.getUserPokemon().getAbility().name);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
expect(chance.value).toBe(30);
}, 20000);
it("Move chance with Serene Grace", async() => {
const moveToUse = Moves.AIR_SLASH;
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SERENE_GRACE);
await game.startBattle([
Species.TOGEKISS
]);
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Check chance of Air Slash with Serene Grace
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.AIR_SLASH);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
expect(chance.value).toBe(60);
}, 20000);
//TODO King's Rock Interaction Unit Test
});

View File

@ -0,0 +1,208 @@
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import {Abilities} from "#enums/abilities";
import {applyAbAttrs ,applyPreAttackAbAttrs,applyPostDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr} from "#app/data/ability";
import {Species} from "#enums/species";
import {
CommandPhase,
MoveEffectPhase,
} from "#app/phases";
import {Mode} from "#app/ui/ui";
import {Stat} from "#app/data/pokemon-stat";
import {Moves} from "#enums/moves";
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
import {Command} from "#app/ui/command-ui-handler";
import * as Utils from "#app/utils";
describe("Abilities - Sheer Force", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
const movesToUse = [Moves.AIR_SLASH, Moves.BIND, Moves.CRUSH_CLAW, Moves.TACKLE];
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ONIX);
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100);
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(movesToUse);
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
});
it("Sheer Force", async() => {
const moveToUse = Moves.AIR_SLASH;
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SHEER_FORCE);
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.AIR_SLASH);
//Verify the move is boosted and has no chance of secondary effects
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power);
expect(chance.value).toBe(0);
expect(power.value).toBe(move.power * 5461/4096);
}, 20000);
it("Sheer Force with exceptions including binding moves", async() => {
const moveToUse = Moves.BIND;
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SHEER_FORCE);
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.BIND);
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power);
expect(chance.value).toBe(-1);
expect(power.value).toBe(move.power);
}, 20000);
it("Sheer Force with moves with no secondary effect", async() => {
const moveToUse = Moves.TACKLE;
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SHEER_FORCE);
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.TACKLE);
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power);
expect(chance.value).toBe(-1);
expect(power.value).toBe(move.power);
}, 20000);
it("Sheer Force Disabling Specific Abilities", async() => {
const moveToUse = Moves.CRUSH_CLAW;
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.COLOR_CHANGE);
vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "KINGS_ROCK", count: 1}]);
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SHEER_FORCE);
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.CRUSH_CLAW);
//Disable color change due to being hit by Sheer Force
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
const user = phase.getUserPokemon();
const target = phase.getTarget();
const opponentType = target.getTypes()[0];
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, power);
applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move));
expect(chance.value).toBe(0);
expect(power.value).toBe(move.power * 5461/4096);
expect(target.getTypes().length).toBe(2);
expect(target.getTypes()[0]).toBe(opponentType);
}, 20000);
//TODO King's Rock Interaction Unit Test
});

View File

@ -0,0 +1,79 @@
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import { Abilities } from "#enums/abilities";
import {applyAbAttrs ,applyPreDefendAbAttrs,IgnoreMoveEffectsAbAttr,MoveEffectChanceMultiplierAbAttr} from "#app/data/ability";
import {Species} from "#enums/species";
import {
CommandPhase,
MoveEffectPhase,
} from "#app/phases";
import {Mode} from "#app/ui/ui";
import {Stat} from "#app/data/pokemon-stat";
import {Moves} from "#enums/moves";
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
import {Command} from "#app/ui/command-ui-handler";
import * as Utils from "#app/utils";
describe("Abilities - Shield Dust", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
const movesToUse = [Moves.AIR_SLASH];
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ONIX);
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SHIELD_DUST);
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100);
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(movesToUse);
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
});
it("Shield Dust", async() => {
const moveToUse = Moves.AIR_SLASH;
await game.startBattle([
Species.PIDGEOT
]);
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1;
expect(game.scene.getParty()[0].formIndex).toBe(0);
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
});
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
});
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Shield Dust negates secondary effect
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
const move = phase.move.getMove();
expect(move.id).toBe(Moves.AIR_SLASH);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false);
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget(),phase.getUserPokemon(),null,null, chance);
expect(chance.value).toBe(0);
}, 20000);
//TODO King's Rock Interaction Unit Test
});