Spaces:
Paused
Paused
| export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = { | |
| airlock: { | |
| inherit: true, | |
| onSwitchIn() {}, | |
| onStart(pokemon) { | |
| pokemon.abilityState.ending = false; | |
| }, | |
| }, | |
| angerpoint: { | |
| inherit: true, | |
| onAfterSubDamage(damage, target, source, move) { | |
| if (!target.hp) return; | |
| if (move && move.effectType === 'Move' && target.getMoveHitData(move).crit) { | |
| target.setBoost({ atk: 6 }); | |
| this.add('-setboost', target, 'atk', 12, '[from] ability: Anger Point'); | |
| } | |
| }, | |
| rating: 1.5, | |
| }, | |
| baddreams: { | |
| inherit: true, | |
| onResidualOrder: 10, | |
| onResidualSubOrder: 10, | |
| }, | |
| blaze: { | |
| onBasePowerPriority: 2, | |
| onBasePower(basePower, attacker, defender, move) { | |
| if (move.type === 'Fire' && attacker.hp <= attacker.maxhp / 3) { | |
| this.debug('Blaze boost'); | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| name: "Blaze", | |
| rating: 2, | |
| num: 66, | |
| }, | |
| cloudnine: { | |
| inherit: true, | |
| onSwitchIn() {}, | |
| onStart(pokemon) { | |
| pokemon.abilityState.ending = false; | |
| }, | |
| }, | |
| colorchange: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (!damage || !target.hp) return; | |
| const type = move.type; | |
| if (target.isActive && move.category !== 'Status' && type !== '???' && !target.hasType(type)) { | |
| if (!target.setType(type)) return false; | |
| this.add('-start', target, 'typechange', type, '[from] ability: Color Change'); | |
| } | |
| }, | |
| onAfterMoveSecondary() {}, | |
| }, | |
| compoundeyes: { | |
| onSourceModifyAccuracyPriority: 9, | |
| onSourceModifyAccuracy(accuracy) { | |
| if (typeof accuracy !== 'number') return; | |
| this.debug('compoundeyes - enhancing accuracy'); | |
| return accuracy * 1.3; | |
| }, | |
| inherit: true, | |
| }, | |
| cutecharm: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact']) { | |
| if (this.randomChance(3, 10)) { | |
| source.addVolatile('attract', this.effectState.target); | |
| } | |
| } | |
| }, | |
| }, | |
| download: { | |
| inherit: true, | |
| onStart(pokemon) { | |
| let totaldef = 0; | |
| let totalspd = 0; | |
| for (const target of pokemon.foes()) { | |
| if (target.volatiles.substitute) continue; | |
| totaldef += target.getStat('def', false, true); | |
| totalspd += target.getStat('spd', false, true); | |
| } | |
| if (totaldef && totaldef >= totalspd) { | |
| this.boost({ spa: 1 }); | |
| } else if (totalspd) { | |
| this.boost({ atk: 1 }); | |
| } | |
| }, | |
| }, | |
| effectspore: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact'] && !source.status) { | |
| const r = this.random(100); | |
| if (r < 10) { | |
| source.setStatus('slp', target); | |
| } else if (r < 20) { | |
| source.setStatus('par', target); | |
| } else if (r < 30) { | |
| source.setStatus('psn', target); | |
| } | |
| } | |
| }, | |
| }, | |
| flamebody: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact']) { | |
| if (this.randomChance(3, 10)) { | |
| source.trySetStatus('brn', target); | |
| } | |
| } | |
| }, | |
| }, | |
| flashfire: { | |
| inherit: true, | |
| onTryHit(target, source, move) { | |
| if (target !== source && move.type === 'Fire') { | |
| if (target.status === 'frz') { | |
| return; | |
| } | |
| if (!target.addVolatile('flashfire')) { | |
| this.add('-immune', target, '[from] ability: Flash Fire'); | |
| } | |
| return null; | |
| } | |
| }, | |
| condition: { | |
| noCopy: true, // doesn't get copied by Baton Pass | |
| onStart(target) { | |
| this.add('-start', target, 'ability: Flash Fire'); | |
| }, | |
| onModifyDamagePhase1(atk, attacker, defender, move) { | |
| if (move.type === 'Fire') { | |
| this.debug('Flash Fire boost'); | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| onEnd(target) { | |
| this.add('-end', target, 'ability: Flash Fire', '[silent]'); | |
| }, | |
| }, | |
| }, | |
| flowergift: { | |
| inherit: true, | |
| onAllyModifyAtk(atk) { | |
| if (this.field.isWeather('sunnyday')) { | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| onAllyModifySpD(spd) { | |
| if (this.field.isWeather('sunnyday')) { | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| flags: { breakable: 1 }, | |
| }, | |
| forecast: { | |
| inherit: true, | |
| flags: { notrace: 1 }, | |
| }, | |
| forewarn: { | |
| inherit: true, | |
| onStart(pokemon) { | |
| let warnMoves: Move[] = []; | |
| let warnBp = 1; | |
| for (const target of pokemon.foes()) { | |
| for (const moveSlot of target.moveSlots) { | |
| const move = this.dex.moves.get(moveSlot.move); | |
| let bp = move.basePower; | |
| if (move.ohko) bp = 160; | |
| if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120; | |
| if (!bp && move.category !== 'Status') bp = 80; | |
| if (bp > warnBp) { | |
| warnMoves = [move]; | |
| warnBp = bp; | |
| } else if (bp === warnBp) { | |
| warnMoves.push(move); | |
| } | |
| } | |
| } | |
| if (!warnMoves.length) return; | |
| const warnMove = this.sample(warnMoves); | |
| this.add('-activate', pokemon, 'ability: Forewarn', warnMove); | |
| }, | |
| }, | |
| frisk: { | |
| inherit: true, | |
| onStart(pokemon) { | |
| const target = pokemon.side.randomFoe(); | |
| if (target?.item && !target.itemState.knockedOff) { | |
| this.add('-item', '', target.getItem().name, '[from] ability: Frisk', `[of] ${pokemon}`); | |
| } | |
| }, | |
| }, | |
| hustle: { | |
| inherit: true, | |
| onSourceModifyAccuracyPriority: 7, | |
| onSourceModifyAccuracy(accuracy, target, source, move) { | |
| if (move.category === 'Physical' && typeof accuracy === 'number') { | |
| return accuracy * 0.8; | |
| } | |
| }, | |
| }, | |
| hydration: { | |
| onWeather(target, source, effect) { | |
| if (effect.id === 'raindance' && target.status) { | |
| this.add('-activate', target, 'ability: Hydration'); | |
| target.cureStatus(); | |
| } | |
| }, | |
| name: "Hydration", | |
| rating: 1.5, | |
| num: 93, | |
| }, | |
| insomnia: { | |
| inherit: true, | |
| rating: 2.5, | |
| }, | |
| intimidate: { | |
| inherit: true, | |
| onStart(pokemon) { | |
| const activated = pokemon.adjacentFoes().some(target => ( | |
| !(target.volatiles['substitute'] || target.volatiles['substitutebroken']?.move === 'uturn') | |
| )); | |
| if (!activated) { | |
| this.hint("In Gen 4, Intimidate does not activate if every target has a Substitute (or the Substitute was just broken by U-turn).", false, pokemon.side); | |
| return; | |
| } | |
| this.add('-ability', pokemon, 'Intimidate', 'boost'); | |
| for (const target of pokemon.adjacentFoes()) { | |
| if (target.volatiles['substitute']) { | |
| this.add('-immune', target); | |
| } else if (target.volatiles['substitutebroken']?.move === 'uturn') { | |
| this.hint("In Gen 4, if U-turn breaks Substitute the incoming Intimidate does nothing."); | |
| } else { | |
| this.boost({ atk: -1 }, target, pokemon, null, true); | |
| } | |
| } | |
| }, | |
| }, | |
| leafguard: { | |
| inherit: true, | |
| onSetStatus(status, target, source, effect) { | |
| if (effect && effect.id === 'rest') { | |
| // do nothing | |
| } else if (this.field.isWeather('sunnyday')) { | |
| return false; | |
| } | |
| }, | |
| }, | |
| lightningrod: { | |
| inherit: true, | |
| onTryHit() {}, | |
| rating: 0, | |
| }, | |
| liquidooze: { | |
| inherit: true, | |
| onSourceTryHeal(damage, target, source, effect) { | |
| this.debug(`Heal is occurring: ${target} <- ${source} :: ${effect.id}`); | |
| const canOoze = ['drain', 'leechseed']; | |
| if (canOoze.includes(effect.id) && this.activeMove?.id !== 'dreameater') { | |
| this.damage(damage, null, null, null, true); | |
| return 0; | |
| } | |
| }, | |
| }, | |
| magicguard: { | |
| onDamage(damage, target, source, effect) { | |
| if (effect.effectType !== 'Move') { | |
| return false; | |
| } | |
| }, | |
| onSetStatus(status, target, source, effect) { | |
| if (effect && effect.id === 'toxicspikes') { | |
| return false; | |
| } | |
| }, | |
| name: "Magic Guard", | |
| rating: 4.5, | |
| num: 98, | |
| }, | |
| minus: { | |
| onModifySpA(spa, pokemon) { | |
| for (const ally of pokemon.allies()) { | |
| if (ally.ability === 'plus') { | |
| return spa * 1.5; | |
| } | |
| } | |
| }, | |
| name: "Minus", | |
| rating: 0, | |
| num: 58, | |
| }, | |
| naturalcure: { | |
| inherit: true, | |
| onCheckShow(pokemon) {}, | |
| onSwitchOut(pokemon) { | |
| if (!pokemon.status || pokemon.status === 'fnt') return; | |
| // Because statused/unstatused pokemon are shown after every switch | |
| // in gen 3-4, Natural Cure's curing is always known to both players | |
| this.add('-curestatus', pokemon, pokemon.status, '[from] ability: Natural Cure'); | |
| pokemon.clearStatus(); | |
| }, | |
| }, | |
| normalize: { | |
| inherit: true, | |
| onModifyMove(move) { | |
| if (move.id !== 'struggle') { | |
| move.type = 'Normal'; | |
| } | |
| }, | |
| }, | |
| overgrow: { | |
| onBasePowerPriority: 2, | |
| onBasePower(basePower, attacker, defender, move) { | |
| if (move.type === 'Grass' && attacker.hp <= attacker.maxhp / 3) { | |
| this.debug('Overgrow boost'); | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| name: "Overgrow", | |
| rating: 2, | |
| num: 65, | |
| }, | |
| pickup: { | |
| name: "Pickup", | |
| rating: 0, | |
| num: 53, | |
| }, | |
| plus: { | |
| onModifySpA(spa, pokemon) { | |
| for (const ally of pokemon.allies()) { | |
| if (ally.ability === 'minus') { | |
| return spa * 1.5; | |
| } | |
| } | |
| }, | |
| name: "Plus", | |
| rating: 0, | |
| num: 57, | |
| }, | |
| poisonpoint: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact']) { | |
| if (this.randomChance(3, 10)) { | |
| source.trySetStatus('psn', target); | |
| } | |
| } | |
| }, | |
| }, | |
| pressure: { | |
| onStart(pokemon) { | |
| this.add('-ability', pokemon, 'Pressure'); | |
| }, | |
| onDeductPP(target, source) { | |
| if (target === source) return; | |
| return 1; | |
| }, | |
| name: "Pressure", | |
| rating: 1.5, | |
| num: 46, | |
| }, | |
| roughskin: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact']) { | |
| this.damage(source.baseMaxhp / 8, source, target); | |
| } | |
| }, | |
| }, | |
| sandveil: { | |
| inherit: true, | |
| onModifyAccuracyPriority: 8, | |
| onModifyAccuracy(accuracy) { | |
| if (typeof accuracy !== 'number') return; | |
| if (this.field.isWeather('sandstorm')) { | |
| this.debug('Sand Veil - decreasing accuracy'); | |
| return accuracy * 0.8; | |
| } | |
| }, | |
| }, | |
| serenegrace: { | |
| inherit: true, | |
| onModifyMove(move) { | |
| if (move.secondaries) { | |
| this.debug('doubling secondary chance'); | |
| for (const secondary of move.secondaries) { | |
| if (secondary.chance) secondary.chance *= 2; | |
| } | |
| } | |
| }, | |
| }, | |
| shedskin: { | |
| inherit: true, | |
| onResidualOrder: 10, | |
| onResidualSubOrder: 3, | |
| }, | |
| simple: { | |
| onModifyBoost(boosts) { | |
| let key: BoostID; | |
| for (key in boosts) { | |
| boosts[key]! *= 2; | |
| } | |
| }, | |
| flags: { breakable: 1 }, | |
| name: "Simple", | |
| rating: 4, | |
| num: 86, | |
| }, | |
| snowcloak: { | |
| inherit: true, | |
| onModifyAccuracyPriority: 8, | |
| onModifyAccuracy(accuracy) { | |
| if (typeof accuracy !== 'number') return; | |
| if (this.field.isWeather('hail')) { | |
| this.debug('Snow Cloak - decreasing accuracy'); | |
| return accuracy * 0.8; | |
| } | |
| }, | |
| }, | |
| speedboost: { | |
| inherit: true, | |
| onResidualOrder: 10, | |
| onResidualSubOrder: 3, | |
| }, | |
| static: { | |
| inherit: true, | |
| onDamagingHit(damage, target, source, move) { | |
| if (damage && move.flags['contact']) { | |
| if (this.randomChance(3, 10)) { | |
| source.trySetStatus('par', target); | |
| } | |
| } | |
| }, | |
| }, | |
| stench: { | |
| name: "Stench", | |
| rating: 0, | |
| num: 1, | |
| }, | |
| stickyhold: { | |
| inherit: true, | |
| onTakeItem(item, pokemon, source) { | |
| if ((source && source !== pokemon) || (this.activeMove && this.activeMove.id === 'knockoff')) { | |
| this.add('-activate', pokemon, 'ability: Sticky Hold'); | |
| return false; | |
| } | |
| }, | |
| }, | |
| stormdrain: { | |
| inherit: true, | |
| onTryHit() {}, | |
| rating: 0, | |
| }, | |
| sturdy: { | |
| inherit: true, | |
| onDamage() {}, | |
| rating: 0, | |
| }, | |
| swarm: { | |
| onBasePowerPriority: 2, | |
| onBasePower(basePower, attacker, defender, move) { | |
| if (move.type === 'Bug' && attacker.hp <= attacker.maxhp / 3) { | |
| this.debug('Swarm boost'); | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| name: "Swarm", | |
| rating: 2, | |
| num: 68, | |
| }, | |
| synchronize: { | |
| inherit: true, | |
| onAfterSetStatus(status, target, source, effect) { | |
| if (!source || source === target) return; | |
| if (effect && effect.id === 'toxicspikes') return; | |
| let id: string = status.id; | |
| if (id === 'slp' || id === 'frz') return; | |
| if (id === 'tox') id = 'psn'; | |
| source.trySetStatus(id, target); | |
| }, | |
| }, | |
| tangledfeet: { | |
| inherit: true, | |
| onModifyAccuracyPriority: 6, | |
| onModifyAccuracy(accuracy, target) { | |
| if (typeof accuracy !== 'number') return; | |
| if (target?.volatiles['confusion']) { | |
| this.debug('Tangled Feet - decreasing accuracy'); | |
| return accuracy * 0.5; | |
| } | |
| }, | |
| }, | |
| thickfat: { | |
| onSourceBasePowerPriority: 1, | |
| onSourceBasePower(basePower, attacker, defender, move) { | |
| if (move.type === 'Ice' || move.type === 'Fire') { | |
| return this.chainModify(0.5); | |
| } | |
| }, | |
| flags: { breakable: 1 }, | |
| name: "Thick Fat", | |
| rating: 3.5, | |
| num: 47, | |
| }, | |
| torrent: { | |
| onBasePowerPriority: 2, | |
| onBasePower(basePower, attacker, defender, move) { | |
| if (move.type === 'Water' && attacker.hp <= attacker.maxhp / 3) { | |
| this.debug('Torrent boost'); | |
| return this.chainModify(1.5); | |
| } | |
| }, | |
| name: "Torrent", | |
| rating: 2, | |
| num: 67, | |
| }, | |
| trace: { | |
| inherit: true, | |
| onUpdate(pokemon) { | |
| if (!this.effectState.seek) return; | |
| const target = pokemon.side.randomFoe(); | |
| if (!target || target.fainted) return; | |
| const ability = target.getAbility(); | |
| const bannedAbilities = ['forecast', 'multitype', 'trace']; | |
| if (bannedAbilities.includes(target.ability)) { | |
| return; | |
| } | |
| if (pokemon.setAbility(ability)) { | |
| this.add('-ability', pokemon, ability, '[from] ability: Trace', `[of] ${target}`); | |
| } | |
| }, | |
| flags: { notrace: 1 }, | |
| }, | |
| unburden: { | |
| inherit: true, | |
| condition: { | |
| onModifySpe(spe, pokemon) { | |
| if ((!pokemon.item || pokemon.itemState.knockedOff) && !pokemon.ignoringAbility()) { | |
| return this.chainModify(2); | |
| } | |
| }, | |
| }, | |
| }, | |
| vitalspirit: { | |
| inherit: true, | |
| rating: 2.5, | |
| }, | |
| wonderguard: { | |
| inherit: true, | |
| onTryHit(target, source, move) { | |
| if (move.id === 'firefang') { | |
| this.hint("In Gen 4, Fire Fang is always able to hit through Wonder Guard.", true, target.side); | |
| return; | |
| } | |
| if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle') return; | |
| this.debug('Wonder Guard immunity: ' + move.id); | |
| if (target.runEffectiveness(move) <= 0) { | |
| this.add('-immune', target, '[from] ability: Wonder Guard'); | |
| return null; | |
| } | |
| }, | |
| }, | |
| }; | |