pokemon-showdown/mods/gennext/abilities.js
Kevin Lau 0bbd3650b7 Normalize ModifySpe calculations with the other Modify-Stat events
The current implementation of ModifyAtk, ModifyDef, etc. is to pass the
actual stat into the event, have event handlers run battle.chainModify(),
ModifySpe passes in a modifier variable, calls battle.chain(), and then
ends with battle.modify() using the modifier variable. The way chainModify
is implemented within the event system also means that it chains together
modifiers in the same manner and finishes with battle.modify(), meaning
that there is no need for ModifySpe to use battle.chain() and
battle.modify() manually.

This also fixes the effect of BattlePokemon#getStat on stats other than
speed.
2015-07-24 12:19:42 -07:00

617 lines
16 KiB
JavaScript

exports.BattleAbilities = {
"drizzle": {
inherit: true,
onStart: function (source) {
this.setWeather('raindance', source, null);
}
},
"swiftswim": {
inherit: true,
onModifySpe: function (spe, pokemon) {
if (this.isWeather(['raindance', 'primordialsea'])) {
return this.chainModify(1.5);
}
}
},
"drought": {
inherit: true,
onStart: function (source) {
this.setWeather('sunnyday', source, null);
}
},
"chlorophyll": {
inherit: true,
onModifySpe: function (spe) {
if (this.isWeather(['sunnyday', 'desolateland'])) {
return this.chainModify(1.5);
}
}
},
"snowwarning": {
inherit: true,
onStart: function (source) {
this.setWeather('hail', source, null);
}
},
"sandstream": {
inherit: true,
onStart: function (source) {
this.setWeather('sandstorm', source, null);
}
},
"sandrush": {
inherit: true,
onModifySpe: function (spe, pokemon) {
if (this.isWeather('sandstorm')) {
return this.chainModify(1.5);
}
}
},
"forecast": {
inherit: true,
onModifyMove: function (move) {
if (move.weather) {
var weather = move.weather;
move.weather = null;
move.onHit = function (target, source) {
this.setWeather(weather, source, this.getAbility('forecast'));
this.weatherData.duration = 0;
};
move.target = 'self';
}
}
},
"thickfat": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
},
onSourceModifyAtk: function (atk, attacker, defender, move) {
if (move.type === 'Ice' || move.type === 'Fire' || move.type === 'Fighting') {
this.add('-message', "The attack was weakened by Thick Fat!");
return this.chainModify(0.5);
}
},
onSourceModifySpA: function (atk, attacker, defender, move) {
if (move.type === 'Ice' || move.type === 'Fire' || move.type === 'Fighting') {
this.add('-message', "The attack was weakened by Thick Fat!");
return this.chainModify(0.5);
}
}
},
"marvelscale": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
}
},
"snowcloak": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
},
onSourceBasePower: function (basePower) {
if (this.isWeather('hail')) {
return basePower * 3 / 4;
}
return basePower * 7 / 8;
},
onModifyAccuracy: function () {}
},
"sandveil": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'sandstorm') return false;
},
onSourceBasePower: function (basePower) {
if (this.isWeather('sandstorm')) {
return basePower * 4 / 5;
}
},
onModifyAccuracy: function () {}
},
"waterveil": {
inherit: true,
onSourceBasePower: function (basePower) {
if (this.isWeather(['raindance', 'primordialsea'])) {
return basePower * 3 / 4;
}
return basePower * 7 / 8;
}
},
"icebody": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
},
onResidual: function (target, source, effect) {
this.heal(target.maxhp / 16);
},
onAfterDamage: function (damage, target, source, move) {
if (move && move.flags['contact'] && this.isWeather('hail')) {
if (this.random(10) < 3) {
source.trySetStatus('frz', target, move);
}
}
},
onWeather: function () {}
},
"flamebody": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
}
},
"static": {
inherit: true,
onAfterDamage: function (damage, target, source, move) {
if (move && move.flags['contact']) {
source.trySetStatus('par', target, move);
}
}
},
"cutecharm": {
inherit: true,
onAfterDamage: function (damage, target, source, move) {
if (move && move.flags['contact']) {
source.addVolatile('Attract', target);
}
}
},
"poisonpoint": {
inherit: true,
onAfterDamage: function (damage, target, source, move) {
if (move && move.flags['contact']) {
source.trySetStatus('psn', target, move);
}
}
},
"flowergift": {
inherit: true,
onModifyMove: function (move) {
if (move.id === 'sunnyday') {
var weather = move.weather;
move.weather = null;
move.onHit = function (target, source) {
this.setWeather(weather, source, this.getAbility('flowergift'));
this.weatherData.duration = 0;
};
move.target = 'self';
move.sideCondition = 'flowergift';
}
},
onUpdate: function (pokemon) {
if (this.isWeather(['sunnyday', 'desolateland'])) {
if (pokemon.isActive && pokemon.speciesid === 'cherrim' && this.effectData.forme !== 'Sunshine') {
this.effectData.forme = 'Sunshine';
this.add('-formechange', pokemon, 'Cherrim-Sunshine', '[msg]');
this.boost({spd:1});
}
} else if (pokemon.isActive && pokemon.speciesid === 'cherrim' && this.effectData.forme) {
delete this.effectData.forme;
this.add('-formechange', pokemon, 'Cherrim', '[msg]');
}
},
effect: {
onSwitchInPriority: 1,
onSwitchIn: function (target) {
if (!target.fainted) {
this.boost({spd:1}, target, target, this.getAbility('flowergift'));
}
target.side.removeSideCondition('flowergift');
}
}
},
"slowstart": {
inherit: true,
effect: {
duration: 3,
onStart: function (target) {
this.add('-start', target, 'Slow Start');
},
onModifyAtk: function (atk, pokemon) {
if (pokemon.ability !== 'slowstart') {
pokemon.removeVolatile('slowstart');
return;
}
return atk / 2;
},
onModifySpe: function (spe, pokemon) {
if (pokemon.ability !== 'slowstart') {
pokemon.removeVolatile('slowstart');
return;
}
return spe / 2;
},
onEnd: function (target) {
this.add('-end', target, 'Slow Start');
}
}
},
"compoundeyes": {
desc: "The accuracy of this Pokemon's moves receives a 60% increase; for example, a 50% accurate move becomes 80% accurate.",
shortDesc: "This Pokemon's moves have their Accuracy boosted to 1.6x.",
onSourceModifyAccuracy: function (accuracy) {
if (typeof accuracy !== 'number') return;
this.debug('compoundeyes - enhancing accuracy');
return accuracy * 1.6;
},
id: "compoundeyes",
name: "Compound Eyes",
rating: 3.5,
num: 14
},
"keeneye": {
desc: "The accuracy of this Pokemon's moves receives a 60% increase; for example, a 50% accurate move becomes 80% accurate.",
shortDesc: "This Pokemon's moves have their Accuracy boosted to 1.6x.",
onModifyMove: function (move) {
if (typeof move.accuracy !== 'number') return;
this.debug('keeneye - enhancing accuracy');
move.accuracy *= 1.6;
},
id: "keeneye",
name: "Keen Eye",
rating: 3.5,
num: 51
},
"solidrock": {
inherit: true,
onSourceModifyDamage: function (damage, attacker, defender, move) {
if (move.typeMod > 0) {
this.add('-message', "The attack was weakened by Solid Rock!");
return this.chainModify(0.5);
}
}
},
"filter": {
inherit: true,
onSourceModifyDamage: function (damage, attacker, defender, move) {
if (move.typeMod > 0) {
this.add('-message', "The attack was weakened by Filter!");
return this.chainModify(0.5);
}
}
},
"heatproof": {
inherit: true,
onSourceBasePower: function (basePower, attacker, defender, move) {
if (move.type === 'Fire') {
this.add('-message', "The attack was weakened by Heatproof!");
return basePower / 2;
}
}
},
"reckless": {
inherit: true,
onBasePower: function (basePower, attacker, defender, move) {
if (move.recoil || move.hasCustomRecoil || attacker.item === 'lifeorb') {
this.debug('Reckless boost');
return basePower * 12 / 10;
}
}
},
"clearbody": {
inherit: true,
onBoost: function (boost, target, source) {
for (var i in boost) {
if (boost[i] < 0) {
delete boost[i];
this.add("-message", target.name + "'s stats were not lowered! (placeholder)");
}
}
}
},
"whitesmoke": {
inherit: true,
onBoost: function (boost, target, source) {
for (var i in boost) {
if (boost[i] < 0) {
delete boost[i];
this.add("-message", target.name + "'s stats were not lowered! (placeholder)");
}
}
}
},
"rockhead": {
inherit: true,
onDamage: function (damage, target, source, effect) {
if (effect && effect.id in {lifeorb: 1, recoil: 1}) return false;
}
},
"download": {
inherit: true,
onStart: function (pokemon) {
if (pokemon.template.baseSpecies === 'Genesect') {
if (!pokemon.getItem().onDrive) return;
}
var foeactive = pokemon.side.foe.active;
var totaldef = 0;
var totalspd = 0;
for (var i = 0; i < foeactive.length; i++) {
if (!foeactive[i] || foeactive[i].fainted) continue;
totaldef += foeactive[i].stats.def;
totalspd += foeactive[i].stats.spd;
}
if (totaldef && totaldef >= totalspd) {
this.boost({spa:1});
} else if (totalspd) {
this.boost({atk:1});
}
}
},
"victorystar": {
inherit: true,
onAllyModifyMove: function (move) {
if (typeof move.accuracy === 'number') {
move.accuracy *= 1.5;
}
}
},
"shellarmor": {
inherit: true,
onDamage: function (damage, target, source, effect) {
if (effect && effect.effectType === 'Move') {
this.add('-message', "Its damage was reduced by Shell Armor!");
damage -= target.maxhp / 10;
if (damage < 0) damage = 0;
return damage;
}
},
onHit: function (target, source, move) {
if (move.id === 'shellsmash') {
target.setAbility('');
}
}
},
"battlearmor": {
inherit: true,
onDamage: function (damage, target, source, effect) {
if (effect && effect.effectType === 'Move') {
this.add('-message', "Its damage was reduced by Battle Armor!");
damage -= target.maxhp / 10;
if (damage < 0) damage = 0;
return damage;
}
}
},
"weakarmor": {
onDamage: function (damage, target, source, effect) {
if (effect && effect.effectType === 'Move') {
this.add('-message', "Its damage was reduced by Weak Armor!");
damage -= target.maxhp / 10;
if (damage < 0) damage = 0;
target.setAbility('');
this.boost({spe: 1});
return damage;
}
},
onAfterDamage: function () {}
},
"magmaarmor": {
inherit: true,
onImmunity: function (type, pokemon) {
if (type === 'hail') return false;
if (type === 'frz') return false;
},
onDamage: function (damage, target, source, effect) {
if (effect && effect.effectType === 'Move') {
damage -= target.maxhp / 10;
if (damage < 0) damage = 0;
if (effect.type === 'Ice' || effect.type === 'Water') {
this.add('-activate', target, 'ability: Magma Armor');
target.setAbility('battlearmor');
damage = 0;
} else {
this.add('-message', "Its damage was reduced by Magma Armor!");
}
return damage;
}
}
},
"multiscale": {
inherit: true,
onSourceModifyDamage: function (damage, source, target, move) {
if (target.hp >= target.maxhp) {
this.add('-message', "The attack was slightly weakened by Multiscale!");
return this.chainModify(2 / 3);
}
}
},
"ironfist": {
inherit: true,
onBasePower: function (basePower, attacker, defender, move) {
if (move.flags['punch']) {
return basePower * 1.33;
}
}
},
"stench": {
inherit: true,
onModifyMove: function (move) {
if (move.category !== "Status") {
this.debug('Adding Stench flinch');
if (!move.secondaries) move.secondaries = [];
for (var i = 0; i < move.secondaries.length; i++) {
if (move.secondaries[i].volatileStatus === 'flinch') return;
}
move.secondaries.push({
chance: 40,
volatileStatus: 'flinch'
});
}
}
},
"aftermath": {
inherit: true,
onAfterDamage: function (damage, target, source, move) {
if (source && source !== target && move && !target.hp) {
this.damage(source.maxhp / 3, source, target, null, true);
}
}
},
"cursedbody": {
desc: "When this Pokemon faints, attacker is Cursed.",
shortDesc: "When this Pokemon faints, attacker is Cursed.",
onFaint: function (target, source, effect) {
if (effect && effect.effectType === 'Move' && source) {
source.addVolatile('curse');
}
},
id: "cursedbody",
name: "Cursed Body",
rating: 3,
num: 130
},
"gluttony": {
inherit: true,
onResidualOrder: 26,
onResidualSubOrder: 1,
onResidual: function (pokemon) {
if (!pokemon.gluttonyFlag && !pokemon.item && this.getItem(pokemon.lastItem).isBerry) {
pokemon.gluttonyFlag = true;
pokemon.setItem(pokemon.lastItem);
this.add("-item", pokemon, pokemon.item, '[from] ability: Gluttony');
}
}
},
"guts": {
inherit: true,
onDamage: function (damage, attacker, defender, effect) {
if (effect && (effect.id === 'brn' || effect.id === 'psn' || effect.id === 'tox')) {
return damage / 2;
}
}
},
"quickfeet": {
inherit: true,
onDamage: function (damage, attacker, defender, effect) {
if (effect && (effect.id === 'brn' || effect.id === 'psn' || effect.id === 'tox')) {
return damage / 2;
}
}
},
"toxicboost": {
inherit: true,
onDamage: function (damage, attacker, defender, effect) {
if (effect && (effect.id === 'psn' || effect.id === 'tox')) {
return damage / 2;
}
}
},
"truant": {
onModifyMove: function (move, pokemon) {
if (!move.self) move.self = {};
if (!move.self.volatileStatus) move.self.volatileStatus = 'truant';
},
effect: {
duration: 2,
onStart: function (pokemon) {
this.add('-start', pokemon, 'Truant');
},
onBeforeMovePriority: 99,
onBeforeMove: function (pokemon, target, move) {
if (pokemon.removeVolatile('truant')) {
this.add('cant', pokemon, 'ability: Truant');
this.heal(pokemon.maxhp / 3);
return false;
}
}
}
},
"flareboost": {
inherit: true,
onDamage: function (damage, defender, attacker, effect) {
if (effect && (effect.id === 'brn')) {
return damage / 2;
}
}
},
"telepathy": {
inherit: true,
onStart: function (target) {
this.add('-start', target, 'move: Imprison');
},
onFoeDisableMove: function (pokemon) {
var foeMoves = this.effectData.target.moveset;
for (var f = 0; f < foeMoves.length; f++) {
pokemon.disableMove(foeMoves[f].id, true);
}
pokemon.maybeDisabled = true;
},
onFoeBeforeMove: function (attacker, defender, move) {
if (attacker.disabledMoves[move.id]) {
this.add('cant', attacker, 'move: Imprison', move);
return false;
}
}
},
"speedboost": {
inherit: true,
onResidualPriority: -1,
onResidual: function (pokemon) {
if (pokemon.activeTurns && !pokemon.volatiles.stall) {
this.boost({spe:1});
}
}
},
"parentalbond": {
inherit: true,
onModifyMove: function (move, pokemon, target) {
if (move.category !== 'Status' && !move.selfdestruct && !move.multihit && ((target.side && target.side.active.length < 2) || move.target in {any:1, normal:1, randomNormal:1})) {
move.multihit = 2;
move.accuracy = true;
pokemon.addVolatile('parentalbond');
}
},
effect: {
duration: 1,
onBasePowerPriority: 8,
onBasePower: function (basePower) {
return this.chainModify(0.5);
}
}
},
"adaptability": {
inherit: true,
onModifyMove: function (move) {},
onBasePower: function (power, attacker, defender, move) {
if (!attacker.hasType(move.type)) {
return this.chainModify(1.33);
}
}
},
"shadowtag": {
onStart: function (pokemon) {
pokemon.addVolatile('shadowtag');
},
effect: {
duration: 2,
onFoeModifyPokemon: function (pokemon) {
if (pokemon.ability !== 'shadowtag') {
pokemon.tryTrap(true);
}
}
},
onBeforeMovePriority: 15,
onBeforeMove: function (pokemon) {
pokemon.removeVolatile('shadowtag');
},
onFoeMaybeTrapPokemon: function (pokemon, source) {
if (!source) source = this.effectData.target;
if (pokemon.ability !== 'shadowtag' && !source.volatiles.shadowtag) {
pokemon.maybeTrapped = true;
}
}
},
"justified": {
inherit: true,
onBasePowerPriority: 100,
onBasePower: function (power, attacker, defender) {
if (power > 100 && !defender.hasType('Dark')) {
this.debug('capping base power at 100');
return 100;
}
}
}
};