From 8a5bbbd25532c73b6c25061fc9040e0d88462360 Mon Sep 17 00:00:00 2001 From: Leonard Craft III Date: Wed, 3 Jan 2024 00:55:17 -0600 Subject: [PATCH] Implement Ability flags (#10048) * Add AbilityFlags interface * Add flags to abilities data (Karthik's script) * Convert isBreakable to its new flag * Convert most of isPermanent to its new flag * Convert Trace to its new flag * Convert Skill Swap to its new flag * Convert Wandering Spirit to the failskillswap flag * Update miscelleneous descriptions that depend on cantsuppress * Convert Entrainment to its flag * Convert Receiver/PoA to its flag * Convert Role Play to its flag * Implement Doodle failure conditions * Various cleanup * Breakable fixes * How did I manage to do this * Allow LightningRod to be breakable in Gen 3 * Implement notransform flag * Tera Shell oopsie * Fix more things after the rebase * And fix Teraform Zero * Update data/abilities.ts * Update data/abilities.ts * Update data/abilities.ts * Update data/mods/partnersincrime/abilities.ts * Update data/abilities.ts * Update data/mods/sharedpower/abilities.ts * Update abilities.ts --------- Co-authored-by: Kris Johnson <11083252+KrisXV@users.noreply.github.com> --- config/formats.ts | 4 +- data/abilities.ts | 534 ++++++++++++++++-------- data/mods/gen3/abilities.ts | 11 +- data/mods/gen4/abilities.ts | 10 +- data/mods/gen4/moves.ts | 3 +- data/mods/gen5/abilities.ts | 1 + data/mods/gen6/abilities.ts | 2 +- data/mods/gen7/abilities.ts | 4 +- data/mods/gen7pokebilities/abilities.ts | 23 +- data/mods/gen7pokebilities/scripts.ts | 2 +- data/mods/gen8/abilities.ts | 4 +- data/mods/gen9dlc1/abilities.ts | 12 +- data/mods/gen9predlc/abilities.ts | 12 +- data/mods/partnersincrime/abilities.ts | 12 +- data/mods/partnersincrime/scripts.ts | 2 +- data/mods/pokebilities/abilities.ts | 36 +- data/mods/pokebilities/scripts.ts | 2 +- data/mods/sharedpower/abilities.ts | 12 +- data/mods/sharedpower/scripts.ts | 2 +- data/mods/ssb/abilities.ts | 21 +- data/mods/vaporemons/abilities.ts | 22 +- data/mods/vaporemons/moves.ts | 4 +- data/mods/vaporemons/scripts.ts | 2 +- data/moves.ts | 58 +-- data/text/abilities.ts | 43 +- data/text/moves.ts | 16 +- server/chat-commands/info.ts | 4 +- sim/battle.ts | 4 +- sim/dex-abilities.ts | 16 +- sim/pokemon.ts | 9 +- test/sim/abilities/commander.js | 38 +- test/sim/abilities/disguise.js | 13 + test/sim/abilities/iceface.js | 23 +- test/sim/abilities/mummy.js | 2 +- test/sim/abilities/protosynthesis.js | 13 + test/sim/abilities/trace.js | 32 ++ test/sim/moves/doodle.js | 52 +++ 37 files changed, 710 insertions(+), 350 deletions(-) create mode 100644 test/sim/moves/doodle.js diff --git a/config/formats.ts b/config/formats.ts index c9effca001f9..e21411738d58 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -1329,14 +1329,14 @@ export const Formats: FormatList = [ if (ally && ally.ability !== pokemon.ability) { if (!pokemon.m.innate && !BAD_ABILITIES.includes(this.toID(ally.ability))) { pokemon.m.innate = 'ability:' + ally.ability; - if (!ngas || ally.getAbility().isPermanent || pokemon.hasItem('Ability Shield')) { + if (!ngas || ally.getAbility().flags['cantsuppress'] || pokemon.hasItem('Ability Shield')) { pokemon.volatiles[pokemon.m.innate] = {id: pokemon.m.innate, target: pokemon}; pokemon.m.startVolatile = true; } } if (!ally.m.innate && !BAD_ABILITIES.includes(this.toID(pokemon.ability))) { ally.m.innate = 'ability:' + pokemon.ability; - if (!ngas || pokemon.getAbility().isPermanent || ally.hasItem('Ability Shield')) { + if (!ngas || pokemon.getAbility().flags['cantsuppress'] || ally.hasItem('Ability Shield')) { ally.volatiles[ally.m.innate] = {id: ally.m.innate, target: ally}; ally.m.startVolatile = true; } diff --git a/data/abilities.ts b/data/abilities.ts index 6dca174a9f77..f663c2f4b720 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -35,6 +35,7 @@ Ratings and how they work: export const Abilities: {[abilityid: string]: AbilityData} = { noability: { isNonstandard: "Past", + flags: {}, name: "No Ability", rating: 0.1, num: 0, @@ -43,6 +44,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.stab = 2; }, + flags: {}, name: "Adaptability", rating: 4, num: 91, @@ -63,18 +65,20 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.typeChangerBoosted === this.effect) return this.chainModify([4915, 4096]); }, + flags: {}, name: "Aerilate", rating: 4, num: 184, }, aftermath: { - name: "Aftermath", onDamagingHitOrder: 1, onDamagingHit(damage, target, source, move) { if (!target.hp && this.checkMoveMakesContact(move, source, target, true)) { this.damage(source.baseMaxhp / 4, source, target); } }, + flags: {}, + name: "Aftermath", rating: 2, num: 106, }, @@ -94,6 +98,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.eachEvent('WeatherChange', this.effect); }, suppressWeather: true, + flags: {}, name: "Air Lock", rating: 1.5, num: 76, @@ -114,6 +119,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5325, 4096]); } }, + flags: {}, name: "Analytic", rating: 2.5, num: 148, @@ -125,6 +131,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 12}, target, target); } }, + flags: {}, name: "Anger Point", rating: 1, num: 83, @@ -160,6 +167,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1, spa: 1, spe: 1, def: -1, spd: -1}, target, target); } }, + flags: {}, name: "Anger Shell", rating: 3, num: 271, @@ -181,6 +189,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Anticipation", rating: 0.5, num: 107, @@ -199,6 +208,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.maybeTrapped = true; } }, + flags: {}, name: "Arena Trap", rating: 5, num: 71, @@ -217,7 +227,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Armor Tail", rating: 2.5, num: 296, @@ -232,7 +242,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Aroma Veil", rating: 2, num: 165, @@ -254,7 +264,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: length}, source, source, this.dex.abilities.get('chillingneigh')); } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "As One (Glastrier)", rating: 3.5, num: 266, @@ -276,7 +286,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spa: length}, source, source, this.dex.abilities.get('grimneigh')); } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "As One (Spectrier)", rating: 3.5, num: 267, @@ -290,7 +300,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (target === source || move.category === 'Status') return; move.hasAuraBreak = true; }, - isBreakable: true, + flags: {breakable: 1}, name: "Aura Break", rating: 1, num: 188, @@ -306,11 +316,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Bad Dreams", rating: 1.5, num: 123, }, ballfetch: { + flags: {}, name: "Ball Fetch", rating: 0, num: 237, @@ -323,13 +335,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5325, 4096]); } }, + flags: {}, name: "Battery", rating: 0, num: 217, }, battlearmor: { onCriticalHit: false, - isBreakable: true, + flags: {breakable: 1}, name: "Battle Armor", rating: 1, num: 4, @@ -344,7 +357,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { source.abilityState.battleBondTriggered = true; } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Battle Bond", rating: 3.5, num: 210, @@ -362,6 +375,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('Beads of Ruin SpD drop'); return this.chainModify(0.75); }, + flags: {}, name: "Beads of Ruin", rating: 4.5, num: 284, @@ -373,6 +387,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({[bestStat]: length}, source); } }, + flags: {}, name: "Beast Boost", rating: 3.5, num: 224, @@ -408,6 +423,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spa: 1}, target, target); } }, + flags: {}, name: "Berserk", rating: 2, num: 201, @@ -422,7 +438,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, - isBreakable: true, + flags: {breakable: 1}, name: "Big Pecks", rating: 0.5, num: 145, @@ -442,6 +458,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Blaze", rating: 2, num: 66, @@ -453,7 +470,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Bulletproof", rating: 3, num: 171, @@ -462,6 +479,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onEatItem(item, pokemon) { this.heal(pokemon.baseMaxhp / 3); }, + flags: {}, name: "Cheek Pouch", rating: 2, num: 167, @@ -472,6 +490,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: length}, source); } }, + flags: {}, name: "Chilling Neigh", rating: 3, num: 264, @@ -482,6 +501,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Chlorophyll", rating: 3, num: 34, @@ -501,7 +521,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add("-fail", target, "unboost", "[from] ability: Clear Body", "[of] " + target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Clear Body", rating: 2, num: 29, @@ -522,6 +542,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.eachEvent('WeatherChange', this.effect); }, suppressWeather: true, + flags: {}, name: "Cloud Nine", rating: 1.5, num: 13, @@ -546,6 +567,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Color Change", rating: 0, num: 16, @@ -561,7 +583,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; }, // Permanent sleep "status" implemented in the relevant sleep-checking effects - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Comatose", rating: 4, num: 213, @@ -570,8 +592,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onUpdate(pokemon) { if (this.gameType !== 'doubles') return; const ally = pokemon.allies()[0]; - if (!ally || pokemon.transformed || - pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') { + if (!ally || pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') { // Handle any edge cases if (pokemon.getVolatile('commanding')) pokemon.removeVolatile('commanding'); return; @@ -592,6 +613,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.removeVolatile('commanding'); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1}, name: "Commander", rating: 0, num: 279, @@ -615,6 +637,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spa: 2}, target, target, null, false, true); } }, + flags: {}, name: "Competitive", rating: 2.5, num: 172, @@ -626,6 +649,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('compoundeyes - enhancing accuracy'); return this.chainModify([5325, 4096]); }, + flags: {}, name: "Compound Eyes", rating: 3, num: 14, @@ -638,13 +662,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { boost[i]! *= -1; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Contrary", rating: 4.5, num: 126, }, corrosion: { // Implemented in sim/pokemon.js:Pokemon#setStatus + flags: {}, name: "Corrosion", rating: 2.5, num: 212, @@ -669,6 +694,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.add('-copyboost', pokemon, ally, '[from] ability: Costar'); }, + flags: {}, name: "Costar", rating: 0, num: 294, @@ -685,6 +711,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: -1}, pokemon, target, null, true); } }, + flags: {}, name: "Cotton Down", rating: 2, num: 238, @@ -718,6 +745,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } }, }, + flags: {}, name: "Cud Chew", rating: 2, num: 291, @@ -729,6 +757,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-clearboost', ally, '[from] ability: Curious Medicine', '[of] ' + pokemon); } }, + flags: {}, name: "Curious Medicine", rating: 0, num: 261, @@ -742,6 +771,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Cursed Body", rating: 2, num: 130, @@ -754,6 +784,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Cute Charm", rating: 0.5, num: 56, @@ -771,12 +802,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Damp", rating: 0.5, num: 6, }, dancer: { + flags: {}, name: "Dancer", // implemented in runMove in scripts.js rating: 1.5, @@ -794,6 +826,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (move.auraBooster !== this.effectState.target) return; return this.chainModify([move.hasAuraBreak ? 3072 : 5448, 4096]); }, + flags: {}, name: "Dark Aura", rating: 3, num: 186, @@ -804,6 +837,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.shieldBoost = true; this.boost({def: 1}, pokemon); }, + flags: {}, name: "Dauntless Shield", rating: 3.5, num: 235, @@ -822,7 +856,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Dazzling", rating: 2.5, num: 219, @@ -840,6 +874,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, + flags: {}, name: "Defeatist", rating: -1, num: 129, @@ -863,6 +898,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 2}, target, target, null, false, true); } }, + flags: {}, name: "Defiant", rating: 3, num: 128, @@ -886,6 +922,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.field.clearWeather(); }, + flags: {}, name: "Delta Stream", rating: 4, num: 191, @@ -909,6 +946,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.field.clearWeather(); }, + flags: {}, name: "Desolate Land", rating: 4.5, num: 190, @@ -916,10 +954,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { disguise: { onDamagePriority: 1, onDamage(damage, target, source, effect) { - if ( - effect && effect.effectType === 'Move' && - ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed - ) { + if (effect?.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id)) { this.add('-activate', target, 'ability: Disguise'); this.effectState.busted = true; return 0; @@ -927,7 +962,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onCriticalHit(target, source, move) { if (!target) return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed) { + if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) { return; } const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6); @@ -938,7 +973,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onEffectiveness(typeMod, target, type, move) { if (!target || move.category === 'Status') return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed) { + if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) { return; } @@ -955,8 +990,10 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon, this.dex.species.get(speciesid)); } }, - isBreakable: true, - isPermanent: true, + flags: { + failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, + breakable: 1, notransform: 1, + }, name: "Disguise", rating: 3.5, num: 209, @@ -975,6 +1012,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1}); } }, + flags: {}, name: "Download", rating: 3.5, num: 88, @@ -994,6 +1032,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Dragon's Maw", rating: 3.5, num: 263, @@ -1006,6 +1045,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.field.setWeather('raindance'); }, + flags: {}, name: "Drizzle", rating: 4, num: 2, @@ -1018,6 +1058,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.field.setWeather('sunnyday'); }, + flags: {}, name: "Drought", rating: 4, num: 70, @@ -1045,12 +1086,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.damage(target.baseMaxhp / 8, target, target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Dry Skin", rating: 3, num: 87, }, earlybird: { + flags: {}, name: "Early Bird", // Implemented in statuses.js rating: 1.5, @@ -1065,7 +1107,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Earth Eater", rating: 3.5, num: 297, @@ -1083,6 +1125,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Effect Spore", rating: 2, num: 27, @@ -1091,6 +1134,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setTerrain('electricterrain'); }, + flags: {}, name: "Electric Surge", rating: 4, num: 226, @@ -1100,13 +1144,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onDamagingHit(damage, target, source, move) { target.addVolatile('charge'); }, + flags: {}, name: "Electromorphosis", rating: 3, num: 280, }, embodyaspectcornerstone: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({def: 1}, pokemon); } @@ -1114,13 +1159,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn() { delete this.effectState.embodied; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Embody Aspect (Cornerstone)", rating: 3.5, num: 304, }, embodyaspecthearthflame: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({atk: 1}, pokemon); } @@ -1128,13 +1174,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn() { delete this.effectState.embodied; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Embody Aspect (Hearthflame)", rating: 3.5, num: 303, }, embodyaspectteal: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({spe: 1}, pokemon); } @@ -1142,13 +1189,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn() { delete this.effectState.embodied; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Embody Aspect (Teal)", rating: 3.5, num: 301, }, embodyaspectwellspring: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({spd: 1}, pokemon); } @@ -1156,6 +1204,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn() { delete this.effectState.embodied; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Embody Aspect (Wellspring)", rating: 3.5, num: 302, @@ -1171,6 +1220,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { target.switchFlag = true; this.add('-activate', target, 'ability: Emergency Exit'); }, + flags: {}, name: "Emergency Exit", rating: 1, num: 194, @@ -1187,6 +1237,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (move.auraBooster !== this.effectState.target) return; return this.chainModify([move.hasAuraBreak ? 3072 : 5448, 4096]); }, + flags: {}, name: "Fairy Aura", rating: 3, num: 187, @@ -1198,7 +1249,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.75); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Filter", rating: 3, num: 111, @@ -1211,6 +1262,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Flame Body", rating: 2, num: 49, @@ -1222,6 +1274,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Flare Boost", rating: 2, num: 138, @@ -1262,7 +1315,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-end', target, 'ability: Flash Fire', '[silent]'); }, }, - isBreakable: true, + flags: {breakable: 1}, name: "Flash Fire", rating: 3.5, num: 18, @@ -1298,7 +1351,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, - isBreakable: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, breakable: 1}, name: "Flower Gift", rating: 1, num: 122, @@ -1337,7 +1390,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Flower Veil", rating: 0, num: 166, @@ -1349,7 +1402,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (move.flags['contact']) mod /= 2; return this.chainModify(mod); }, - isBreakable: true, + flags: {breakable: 1}, name: "Fluffy", rating: 3.5, num: 218, @@ -1382,6 +1435,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.formeChange(forme, this.effect, false, '[msg]'); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1}, name: "Forecast", rating: 2, num: 59, @@ -1410,19 +1464,20 @@ export const Abilities: {[abilityid: string]: AbilityData} = { const [warnMoveName, warnTarget] = this.sample(warnMoves); this.add('-activate', pokemon, 'ability: Forewarn', warnMoveName, '[of] ' + warnTarget); }, + flags: {}, name: "Forewarn", rating: 0.5, num: 108, }, friendguard: { - name: "Friend Guard", onAnyModifyDamage(damage, source, target, move) { if (target !== this.effectState.target && target.isAlly(this.effectState.target)) { this.debug('Friend Guard weaken'); return this.chainModify(0.75); } }, - isBreakable: true, + flags: {breakable: 1}, + name: "Friend Guard", rating: 0, num: 132, }, @@ -1434,6 +1489,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Frisk", rating: 1.5, num: 119, @@ -1453,6 +1509,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add("-fail", target, "unboost", "[from] ability: Full Metal Body", "[of] " + target); } }, + flags: {}, name: "Full Metal Body", rating: 2, num: 230, @@ -1462,7 +1519,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyDef(def) { return this.chainModify(2); }, - isBreakable: true, + flags: {breakable: 1}, name: "Fur Coat", rating: 4, num: 169, @@ -1471,6 +1528,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyPriority(priority, pokemon, target, move) { if (move?.type === 'Flying' && pokemon.hp === pokemon.maxhp) return priority + 1; }, + flags: {}, name: "Gale Wings", rating: 1.5, num: 177, @@ -1491,21 +1549,22 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.typeChangerBoosted === this.effect) return this.chainModify([4915, 4096]); }, + flags: {}, name: "Galvanize", rating: 4, num: 206, }, gluttony: { - name: "Gluttony", - rating: 1.5, - num: 82, onStart(pokemon) { pokemon.abilityState.gluttony = true; }, onDamage(item, pokemon) { pokemon.abilityState.gluttony = true; }, - + flags: {}, + name: "Gluttony", + rating: 1.5, + num: 82, }, goodasgold: { onTryHit(target, source, move) { @@ -1514,7 +1573,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Good as Gold", rating: 5, num: 283, @@ -1526,6 +1585,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: -1}, source, target, null, true); } }, + flags: {}, name: "Gooey", rating: 2, num: 183, @@ -1568,6 +1628,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onEnd(pokemon) { pokemon.abilityState.choiceLock = ""; }, + flags: {}, name: "Gorilla Tactics", rating: 4.5, num: 255, @@ -1577,7 +1638,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyDef(pokemon) { if (this.field.isTerrain('grassyterrain')) return this.chainModify(1.5); }, - isBreakable: true, + flags: {breakable: 1}, name: "Grass Pelt", rating: 0.5, num: 179, @@ -1586,6 +1647,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setTerrain('grassyterrain'); }, + flags: {}, name: "Grassy Surge", rating: 4, num: 229, @@ -1596,6 +1658,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spa: length}, source); } }, + flags: {}, name: "Grim Neigh", rating: 3, num: 265, @@ -1612,14 +1675,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1}, target, target, null, false, true); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Guard Dog", rating: 2, num: 275, }, gulpmissile: { onDamagingHit(damage, target, source, move) { - if (!source.hp || !source.isActive || target.transformed || target.isSemiInvulnerable()) return; + if (!source.hp || !source.isActive || target.isSemiInvulnerable()) return; if (['cramorantgulping', 'cramorantgorging'].includes(target.species.id)) { this.damage(source.baseMaxhp / 4, source, target); if (target.species.id === 'cramorantgulping') { @@ -1632,15 +1695,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, // The Dive part of this mechanic is implemented in Dive's `onTryMove` in moves.ts onSourceTryPrimaryHit(target, source, effect) { - if ( - effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && - source.species.name === 'Cramorant' && !source.transformed - ) { + if (effect?.id === 'surf' && source.hasAbility('gulpmissile') && source.species.name === 'Cramorant') { const forme = source.hp <= source.maxhp / 2 ? 'cramorantgorging' : 'cramorantgulping'; source.formeChange(forme, effect); } }, - isPermanent: true, + flags: {cantsuppress: 1, notransform: 1}, name: "Gulp Missile", rating: 2.5, num: 241, @@ -1652,6 +1712,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Guts", rating: 3.5, num: 62, @@ -1669,12 +1730,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5461, 4096]); } }, + flags: {}, name: "Hadron Engine", rating: 4.5, num: 289, }, harvest: { - name: "Harvest", onResidualOrder: 28, onResidualSubOrder: 2, onResidual(pokemon) { @@ -1686,11 +1747,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, + name: "Harvest", rating: 2.5, num: 139, }, healer: { - name: "Healer", onResidualOrder: 5, onResidualSubOrder: 3, onResidual(pokemon) { @@ -1701,6 +1763,8 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, + name: "Healer", rating: 0, num: 131, }, @@ -1724,7 +1788,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return damage / 2; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Heatproof", rating: 2, num: 85, @@ -1734,12 +1798,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyWeight(weighthg) { return weighthg * 2; }, - isBreakable: true, + flags: {breakable: 1}, name: "Heavy Metal", rating: 0, num: 134, }, honeygather: { + flags: {}, name: "Honey Gather", rating: 0, num: 118, @@ -1750,6 +1815,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.heal(ally.baseMaxhp / 4, ally, pokemon); } }, + flags: {}, name: "Hospitality", rating: 0, num: 299, @@ -1759,6 +1825,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyAtk(atk) { return this.chainModify(2); }, + flags: {}, name: "Huge Power", rating: 5, num: 37, @@ -1766,10 +1833,11 @@ export const Abilities: {[abilityid: string]: AbilityData} = { hungerswitch: { onResidualOrder: 29, onResidual(pokemon) { - if (pokemon.species.baseSpecies !== 'Morpeko' || pokemon.transformed || pokemon.terastallized) return; + if (pokemon.species.baseSpecies !== 'Morpeko' || pokemon.terastallized) return; const targetForme = pokemon.species.name === 'Morpeko' ? 'Morpeko-Hangry' : 'Morpeko'; pokemon.formeChange(targetForme); }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Hunger Switch", rating: 1, num: 258, @@ -1786,6 +1854,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([3277, 4096]); } }, + flags: {}, name: "Hustle", rating: 3.5, num: 55, @@ -1800,6 +1869,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.cureStatus(); } }, + flags: {}, name: "Hydration", rating: 1.5, num: 93, @@ -1814,7 +1884,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, - isBreakable: true, + flags: {breakable: 1}, name: "Hyper Cutter", rating: 1.5, num: 52, @@ -1828,14 +1898,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onImmunity(type, pokemon) { if (type === 'hail') return false; }, + flags: {}, name: "Ice Body", rating: 1, num: 115, }, iceface: { onStart(pokemon) { - if (this.field.isWeather(['hail', 'snow']) && - pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') { this.add('-activate', pokemon, 'ability: Ice Face'); this.effectState.busted = false; pokemon.formeChange('Eiscue', this.effect, true); @@ -1843,10 +1913,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onDamagePriority: 1, onDamage(damage, target, source, effect) { - if ( - effect && effect.effectType === 'Move' && effect.category === 'Physical' && - target.species.id === 'eiscue' && !target.transformed - ) { + if (effect?.effectType === 'Move' && effect.category === 'Physical' && target.species.id === 'eiscue') { this.add('-activate', target, 'ability: Ice Face'); this.effectState.busted = true; return 0; @@ -1854,14 +1921,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onCriticalHit(target, type, move) { if (!target) return; - if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed) return; + if (move.category !== 'Physical' || target.species.id !== 'eiscue') return; if (target.volatiles['substitute'] && !(move.flags['bypasssub'] || move.infiltrates)) return; if (!target.runImmunity(move.type)) return; return false; }, onEffectiveness(typeMod, target, type, move) { if (!target) return; - if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed) return; + if (move.category !== 'Physical' || target.species.id !== 'eiscue') return; const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6); if (hitSub) return; @@ -1878,15 +1945,16 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // snow/hail resuming because Cloud Nine/Air Lock ended does not trigger Ice Face if ((sourceEffect as Ability)?.suppressWeather) return; if (!pokemon.hp) return; - if (this.field.isWeather(['hail', 'snow']) && - pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') { this.add('-activate', pokemon, 'ability: Ice Face'); this.effectState.busted = false; pokemon.formeChange('Eiscue', this.effect, true); } }, - isBreakable: true, - isPermanent: true, + flags: { + failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, + breakable: 1, notransform: 1, + }, name: "Ice Face", rating: 3, num: 248, @@ -1897,7 +1965,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Ice Scales", rating: 4, num: 246, @@ -1915,7 +1983,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.ignoreEvasion = true; }, - isBreakable: true, + flags: {breakable: 1}, name: "Illuminate", rating: 0.5, num: 35, @@ -1957,6 +2025,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onFaint(pokemon) { pokemon.illusion = null; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1}, name: "Illusion", rating: 4.5, num: 149, @@ -1975,7 +2044,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Immunity", rating: 2, num: 17, @@ -1996,6 +2065,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.effectState.switchingIn = false; }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1}, name: "Imposter", rating: 5, num: 150, @@ -2004,18 +2074,20 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.infiltrates = true; }, + flags: {}, name: "Infiltrator", rating: 2.5, num: 151, }, innardsout: { - name: "Innards Out", onDamagingHitOrder: 1, onDamagingHit(damage, target, source, move) { if (!target.hp) { this.damage(target.getUndynamaxedHP(damage), source, target); } }, + flags: {}, + name: "Innards Out", rating: 4, num: 215, }, @@ -2029,7 +2101,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-fail', target, 'unboost', 'Attack', '[from] ability: Inner Focus', '[of] ' + target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Inner Focus", rating: 1, num: 39, @@ -2054,7 +2126,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Insomnia", rating: 1.5, num: 15, @@ -2074,6 +2146,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Intimidate", rating: 3.5, num: 22, @@ -2084,6 +2157,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.swordBoost = true; this.boost({atk: 1}, pokemon); }, + flags: {}, name: "Intrepid Sword", rating: 4, num: 234, @@ -2095,6 +2169,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.damage(source.baseMaxhp / 8, source, target); } }, + flags: {}, name: "Iron Barbs", rating: 2.5, num: 160, @@ -2107,6 +2182,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([4915, 4096]); } }, + flags: {}, name: "Iron Fist", rating: 3, num: 89, @@ -2117,6 +2193,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1}); } }, + flags: {}, name: "Justified", rating: 2.5, num: 154, @@ -2134,7 +2211,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.ignoreEvasion = true; }, - isBreakable: true, + flags: {breakable: 1}, name: "Keen Eye", rating: 0.5, num: 51, @@ -2144,6 +2221,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(pokemon) { this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon); }, + flags: {}, name: "Klutz", rating: -1, num: 103, @@ -2163,14 +2241,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Leaf Guard", rating: 0.5, num: 102, }, levitate: { // airborneness implemented in sim/pokemon.js:Pokemon#isGrounded - isBreakable: true, + flags: {breakable: 1}, name: "Levitate", rating: 3.5, num: 26, @@ -2189,6 +2267,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn() { delete this.effectState.libero; }, + flags: {}, name: "Libero", rating: 4, num: 236, @@ -2197,7 +2276,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyWeight(weighthg) { return this.trunc(weighthg / 2); }, - isBreakable: true, + flags: {breakable: 1}, name: "Light Metal", rating: 1, num: 135, @@ -2222,7 +2301,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.effectState.target; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Lightning Rod", rating: 3, num: 31, @@ -2241,7 +2320,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Limber", rating: 2, num: 7, @@ -2249,7 +2328,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { lingeringaroma: { onDamagingHit(damage, target, source, move) { const sourceAbility = source.getAbility(); - if (sourceAbility.isPermanent || sourceAbility.id === 'lingeringaroma') { + if (sourceAbility.flags['cantsuppress'] || sourceAbility.id === 'lingeringaroma') { return; } if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { @@ -2259,6 +2338,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Lingering Aroma", rating: 2, num: 268, @@ -2272,6 +2352,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return 0; } }, + flags: {}, name: "Liquid Ooze", rating: 2.5, num: 64, @@ -2283,6 +2364,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { move.type = 'Water'; } }, + flags: {}, name: "Liquid Voice", rating: 1.5, num: 204, @@ -2291,12 +2373,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { delete move.flags['contact']; }, + flags: {}, name: "Long Reach", rating: 1, num: 203, }, magicbounce: { - name: "Magic Bounce", onTryHitPriority: 1, onTryHit(target, source, move) { if (target === source || move.hasBounced || !move.flags['reflectable']) { @@ -2321,7 +2403,8 @@ export const Abilities: {[abilityid: string]: AbilityData} = { condition: { duration: 1, }, - isBreakable: true, + flags: {breakable: 1}, + name: "Magic Bounce", rating: 4, num: 156, }, @@ -2332,6 +2415,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, + flags: {}, name: "Magic Guard", rating: 4, num: 98, @@ -2350,6 +2434,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-item', source, yourItem, '[from] ability: Magician', '[of] ' + target); } }, + flags: {}, name: "Magician", rating: 1, num: 170, @@ -2364,7 +2449,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onImmunity(type, pokemon) { if (type === 'frz') return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Magma Armor", rating: 0.5, num: 40, @@ -2382,6 +2467,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.maybeTrapped = true; } }, + flags: {}, name: "Magnet Pull", rating: 4, num: 42, @@ -2393,7 +2479,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Marvel Scale", rating: 2.5, num: 63, @@ -2405,6 +2491,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Mega Launcher", rating: 3, num: 178, @@ -2413,6 +2500,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyCritRatio(critRatio, source, target) { if (target && ['psn', 'tox'].includes(target.status)) return 5; }, + flags: {}, name: "Merciless", rating: 1.5, num: 196, @@ -2449,6 +2537,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-end', pokemon, 'typechange', '[silent]'); } }, + flags: {}, name: "Mimicry", rating: 0, num: 250, @@ -2472,6 +2561,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { move.ignoreImmunity['Normal'] = true; } }, + flags: {breakable: 1}, name: "Mind's Eye", rating: 0, num: 300, @@ -2485,6 +2575,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Minus", rating: 0, num: 58, @@ -2507,7 +2598,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, - isBreakable: true, + flags: {breakable: 1}, name: "Mirror Armor", rating: 2, num: 240, @@ -2516,6 +2607,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setTerrain('mistyterrain'); }, + flags: {}, name: "Misty Surge", rating: 3.5, num: 228, @@ -2527,6 +2619,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.ignoreAbility = true; }, + flags: {}, name: "Mold Breaker", rating: 3, num: 104, @@ -2560,6 +2653,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost(boost, pokemon, pokemon); }, + flags: {}, name: "Moody", rating: 5, num: 141, @@ -2573,7 +2667,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Motor Drive", rating: 3, num: 78, @@ -2584,6 +2678,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: length}, source); } }, + flags: {}, name: "Moxie", rating: 3, num: 153, @@ -2595,23 +2690,22 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Multiscale", rating: 3.5, num: 136, }, multitype: { // Multitype's type-changing itself is implemented in statuses.js - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Multitype", rating: 4, num: 121, }, mummy: { - name: "Mummy", onDamagingHit(damage, target, source, move) { const sourceAbility = source.getAbility(); - if (sourceAbility.isPermanent || sourceAbility.id === 'mummy') { + if (sourceAbility.flags['cantsuppress'] || sourceAbility.id === 'mummy') { return; } if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { @@ -2621,6 +2715,8 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, + name: "Mummy", rating: 2, num: 152, }, @@ -2636,6 +2732,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { move.ignoreAbility = true; } }, + flags: {}, name: "Mycelium Might", rating: 2, num: 298, @@ -2718,6 +2815,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // (once you know a Pokemon has Natural Cure, its cures are always known) if (!pokemon.showCure) pokemon.showCure = undefined; }, + flags: {}, name: "Natural Cure", rating: 2.5, num: 30, @@ -2728,6 +2826,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5120, 4096]); } }, + flags: {}, name: "Neuroforce", rating: 2.5, num: 233, @@ -2735,7 +2834,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { neutralizinggas: { // Ability suppression implemented in sim/pokemon.ts:Pokemon#ignoringAbility onPreStart(pokemon) { - if (pokemon.transformed) return; this.add('-ability', pokemon, 'Neutralizing Gas'); pokemon.abilityState.ending = false; const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; @@ -2781,7 +2879,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.speedSort(sortedActive); for (const pokemon of sortedActive) { if (pokemon !== source) { - if (pokemon.getAbility().isPermanent) continue; // does not interact with e.g Ice Face, Zen Mode + if (pokemon.getAbility().flags['cantsuppress']) continue; // does not interact with e.g Ice Face, Zen Mode if (pokemon.hasItem('abilityshield')) continue; // don't restart abilities that weren't suppressed // Will be suppressed by Pokemon#ignoringAbility if needed @@ -2792,6 +2890,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Neutralizing Gas", rating: 3.5, num: 256, @@ -2807,6 +2906,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return accuracy; }, + flags: {}, name: "No Guard", rating: 4, num: 99, @@ -2828,6 +2928,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.typeChangerBoosted === this.effect) return this.chainModify([4915, 4096]); }, + flags: {}, name: "Normalize", rating: 0, num: 96, @@ -2860,7 +2961,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-fail', target, 'unboost', 'Attack', '[from] ability: Oblivious', '[of] ' + target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Oblivious", rating: 1.5, num: 12, @@ -2879,6 +2980,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (Object.keys(positiveBoosts).length < 1) return; this.boost(positiveBoosts, pokemon); }, + flags: {}, name: "Opportunist", rating: 3, num: 290, @@ -2898,6 +3000,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5461, 4096]); } }, + flags: {}, name: "Orichalcum Pulse", rating: 4.5, num: 288, @@ -2913,7 +3016,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Overcoat", rating: 2, num: 142, @@ -2933,6 +3036,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Overgrow", rating: 2, num: 65, @@ -2958,7 +3062,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-fail', target, 'unboost', 'Attack', '[from] ability: Own Tempo', '[of] ' + target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Own Tempo", rating: 1.5, num: 20, @@ -2977,6 +3081,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return secondaries.filter(effect => effect.volatileStatus === 'flinch'); } }, + flags: {}, name: "Parental Bond", rating: 4.5, num: 185, @@ -3017,7 +3122,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Pastel Veil", rating: 2, num: 257, @@ -3036,6 +3141,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.addVolatile('perishsong'); } }, + flags: {}, name: "Perish Body", rating: 1, num: 253, @@ -3058,6 +3164,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-item', target, yourItem, '[from] ability: Pickpocket', '[of] ' + source); } }, + flags: {}, name: "Pickpocket", rating: 1, num: 124, @@ -3077,6 +3184,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-item', pokemon, this.dex.items.get(item), '[from] ability: Pickup'); pokemon.setItem(item); }, + flags: {}, name: "Pickup", rating: 0.5, num: 53, @@ -3097,6 +3205,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.typeChangerBoosted === this.effect) return this.chainModify([4915, 4096]); }, + flags: {}, name: "Pixilate", rating: 4, num: 182, @@ -3110,6 +3219,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Plus", rating: 0, num: 57, @@ -3122,6 +3232,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, + flags: {}, name: "Poison Heal", rating: 4, num: 90, @@ -3134,6 +3245,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Poison Point", rating: 1.5, num: 38, @@ -3145,6 +3257,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { target.addVolatile('confusion'); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1}, name: "Poison Puppeteer", rating: 3, num: 310, @@ -3159,6 +3272,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Poison Touch", rating: 2, num: 143, @@ -3178,7 +3292,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.maxhp = newMaxHP; this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Power Construct", rating: 5, num: 211, @@ -3187,14 +3301,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onAllyFaint(target) { if (!this.effectState.target.hp) return; const ability = target.getAbility(); - const additionalBannedAbilities = [ - 'noability', 'commander', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', - ]; - if (target.getAbility().isPermanent || additionalBannedAbilities.includes(target.ability)) return; + if (ability.flags['noreceiver'] || ability.id === 'noability') return; if (this.effectState.target.setAbility(ability)) { this.add('-ability', this.effectState.target, ability, '[from] ability: Power of Alchemy', '[of] ' + target); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1}, name: "Power of Alchemy", rating: 0, num: 223, @@ -3207,6 +3319,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5325, 4096]); } }, + flags: {}, name: "Power Spot", rating: 0, num: 249, @@ -3218,6 +3331,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return priority + 1; } }, + flags: {}, name: "Prankster", rating: 4, num: 158, @@ -3230,6 +3344,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (target.isAlly(source)) return; return 1; }, + flags: {}, name: "Pressure", rating: 2.5, num: 46, @@ -3253,6 +3368,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.field.clearWeather(); }, + flags: {}, name: "Primordial Sea", rating: 4.5, num: 189, @@ -3264,6 +3380,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.75); } }, + flags: {}, name: "Prism Armor", rating: 3, num: 232, @@ -3274,6 +3391,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // most of the implementation is in Battle#getTarget move.tracksTarget = move.target !== 'scripted'; }, + flags: {}, name: "Propeller Tail", rating: 0, num: 239, @@ -3292,6 +3410,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchIn(pokemon) { delete this.effectState.protean; }, + flags: {}, name: "Protean", rating: 4, num: 168, @@ -3301,7 +3420,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon); }, onWeatherChange(pokemon) { - if (pokemon.transformed) return; // Protosynthesis is not affected by Utility Umbrella if (this.field.isWeather('sunnyday')) { pokemon.addVolatile('protosynthesis'); @@ -3358,6 +3476,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-end', pokemon, 'Protosynthesis'); }, }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Protosynthesis", rating: 3, num: 281, @@ -3366,6 +3485,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setTerrain('psychicterrain'); }, + flags: {}, name: "Psychic Surge", rating: 4, num: 227, @@ -3384,7 +3504,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Punk Rock", rating: 3.5, num: 244, @@ -3394,6 +3514,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyAtk(atk) { return this.chainModify(2); }, + flags: {}, name: "Pure Power", rating: 5, num: 74, @@ -3425,7 +3546,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Purifying Salt", rating: 4, num: 272, @@ -3435,7 +3556,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon); }, onTerrainChange(pokemon) { - if (pokemon.transformed) return; if (this.field.isTerrain('electricterrain')) { pokemon.addVolatile('quarkdrive'); } else if (!pokemon.volatiles['quarkdrive']?.fromBooster) { @@ -3491,6 +3611,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-end', pokemon, 'Quark Drive'); }, }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, name: "Quark Drive", rating: 3, num: 282, @@ -3509,7 +3630,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Queenly Majesty", rating: 2.5, num: 214, @@ -3522,6 +3643,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return 0.1; } }, + flags: {}, name: "Quick Draw", rating: 2.5, num: 259, @@ -3532,6 +3654,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Quick Feet", rating: 2.5, num: 95, @@ -3543,6 +3666,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.heal(target.baseMaxhp / 16); } }, + flags: {}, name: "Rain Dish", rating: 1.5, num: 44, @@ -3558,6 +3682,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: 1}); } }, + flags: {}, name: "Rattled", rating: 1, num: 155, @@ -3566,14 +3691,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onAllyFaint(target) { if (!this.effectState.target.hp) return; const ability = target.getAbility(); - const additionalBannedAbilities = [ - 'noability', 'commander', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', - ]; - if (target.getAbility().isPermanent || additionalBannedAbilities.includes(target.ability)) return; + if (ability.flags['noreceiver'] || ability.id === 'noability') return; if (this.effectState.target.setAbility(ability)) { this.add('-ability', this.effectState.target, ability, '[from] ability: Receiver', '[of] ' + target); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1}, name: "Receiver", rating: 0, num: 222, @@ -3586,6 +3709,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([4915, 4096]); } }, + flags: {}, name: "Reckless", rating: 3, num: 120, @@ -3606,6 +3730,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.typeChangerBoosted === this.effect) return this.chainModify([4915, 4096]); }, + flags: {}, name: "Refrigerate", rating: 4, num: 174, @@ -3614,6 +3739,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onSwitchOut(pokemon) { pokemon.heal(pokemon.baseMaxhp / 3); }, + flags: {}, name: "Regenerator", rating: 4.5, num: 144, @@ -3652,6 +3778,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // Record if the pokemon ate a berry to resist the attack pokemon.abilityState.berryWeaken = weakenBerries.includes(item.name); }, + flags: {}, name: "Ripen", rating: 2, num: 247, @@ -3669,13 +3796,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Rivalry", rating: 0, num: 79, }, rkssystem: { // RKS System's type-changing itself is implemented in statuses.js - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "RKS System", rating: 4, num: 225, @@ -3687,6 +3815,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (this.activeMove.id !== 'struggle') return null; } }, + flags: {}, name: "Rock Head", rating: 3, num: 69, @@ -3706,6 +3835,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Rocky Payload", rating: 3.5, num: 276, @@ -3717,11 +3847,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.damage(source.baseMaxhp / 8, source, target); } }, + flags: {}, name: "Rough Skin", rating: 2.5, num: 24, }, runaway: { + flags: {}, name: "Run Away", rating: 0, num: 50, @@ -3739,6 +3871,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onImmunity(type, pokemon) { if (type === 'sandstorm') return false; }, + flags: {}, name: "Sand Force", rating: 2, num: 159, @@ -3752,6 +3885,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onImmunity(type, pokemon) { if (type === 'sandstorm') return false; }, + flags: {}, name: "Sand Rush", rating: 3, num: 146, @@ -3760,6 +3894,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onDamagingHit(damage, target, source, move) { this.field.setWeather('sandstorm'); }, + flags: {}, name: "Sand Spit", rating: 1, num: 245, @@ -3768,6 +3903,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setWeather('sandstorm'); }, + flags: {}, name: "Sand Stream", rating: 4, num: 45, @@ -3784,7 +3920,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([3277, 4096]); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Sand Veil", rating: 1.5, num: 8, @@ -3805,7 +3941,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1}, this.effectState.target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Sap Sipper", rating: 3, num: 157, @@ -3839,7 +3975,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Schooling", rating: 3, num: 208, @@ -3859,6 +3995,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-fail', target, 'unboost', 'Attack', '[from] ability: Scrappy', '[of] ' + target); } }, + flags: {}, name: "Scrappy", rating: 3, num: 113, @@ -3878,6 +4015,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Screen Cleaner", rating: 2, num: 251, @@ -3886,6 +4024,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onDamagingHit(damage, target, source, move) { this.field.setTerrain('grassyterrain'); }, + flags: {}, name: "Seed Sower", rating: 2.5, num: 269, @@ -3901,6 +4040,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } if (move.self?.chance) move.self.chance *= 2; }, + flags: {}, name: "Serene Grace", rating: 3.5, num: 32, @@ -3912,6 +4052,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, + flags: {}, name: "Shadow Shield", rating: 3.5, num: 231, @@ -3929,6 +4070,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.maybeTrapped = true; } }, + flags: {}, name: "Shadow Tag", rating: 5, num: 23, @@ -3941,6 +4083,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Sharpness", rating: 3.5, num: 292, @@ -3955,6 +4098,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.cureStatus(); } }, + flags: {}, name: "Shed Skin", rating: 3, num: 61, @@ -3974,13 +4118,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onBasePower(basePower, pokemon, target, move) { if (move.hasSheerForce) return this.chainModify([5325, 4096]); }, + flags: {}, name: "Sheer Force", rating: 3.5, num: 125, }, shellarmor: { onCriticalHit: false, - isBreakable: true, + flags: {breakable: 1}, name: "Shell Armor", rating: 1, num: 75, @@ -3990,7 +4135,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('Shield Dust prevent secondary'); return secondaries.filter(effect => !!(effect.self || effect.dustproof)); }, - isBreakable: true, + flags: {breakable: 1}, name: "Shield Dust", rating: 2, num: 19, @@ -4034,7 +4179,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-immune', target, '[from] ability: Shields Down'); return null; }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Shields Down", rating: 3, num: 197, @@ -4047,7 +4192,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { boost[i]! *= 2; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Simple", rating: 4, num: 86, @@ -4061,6 +4206,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { delete move.multiaccuracy; } }, + flags: {}, name: "Skill Link", rating: 3, num: 92, @@ -4091,6 +4237,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-end', target, 'Slow Start'); }, }, + flags: {}, name: "Slow Start", rating: -1, num: 112, @@ -4101,6 +4248,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Slush Rush", rating: 3, num: 202, @@ -4112,6 +4260,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Sniper", rating: 2, num: 97, @@ -4128,7 +4277,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([3277, 4096]); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Snow Cloak", rating: 1.5, num: 81, @@ -4137,6 +4286,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(source) { this.field.setWeather('snow'); }, + flags: {}, name: "Snow Warning", rating: 4, num: 117, @@ -4154,6 +4304,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.damage(target.baseMaxhp / 8, target, target); } }, + flags: {}, name: "Solar Power", rating: 2, num: 94, @@ -4165,7 +4316,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.75); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Solid Rock", rating: 3, num: 116, @@ -4175,6 +4326,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onAnyFaint() { this.boost({spa: 1}, this.effectState.target); }, + flags: {}, name: "Soul-Heart", rating: 3.5, num: 220, @@ -4191,7 +4343,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-immune', this.effectState.target, '[from] ability: Soundproof'); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Soundproof", rating: 2, num: 43, @@ -4204,6 +4356,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: 1}); } }, + flags: {}, name: "Speed Boost", rating: 4.5, num: 3, @@ -4223,12 +4376,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Stakeout", rating: 4.5, num: 198, }, stall: { onFractionalPriority: -0.1, + flags: {}, name: "Stall", rating: -1, num: 100, @@ -4239,6 +4394,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // most of the implementation is in Battle#getTarget move.tracksTarget = move.target !== 'scripted'; }, + flags: {}, name: "Stalwart", rating: 0, num: 242, @@ -4247,6 +4403,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onDamagingHit(damage, target, source, effect) { this.boost({def: 1}); }, + flags: {}, name: "Stamina", rating: 4, num: 192, @@ -4259,7 +4416,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { const targetForme = (move.id === 'kingsshield' ? 'Aegislash' : 'Aegislash-Blade'); if (attacker.species.name !== targetForme) attacker.formeChange(targetForme); }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Stance Change", rating: 4, num: 176, @@ -4272,6 +4429,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Static", rating: 2, num: 9, @@ -4280,6 +4438,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onFlinch(pokemon) { this.boost({spe: 1}); }, + flags: {}, name: "Steadfast", rating: 1, num: 80, @@ -4290,6 +4449,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: 6}); } }, + flags: {}, name: "Steam Engine", rating: 2, num: 243, @@ -4309,6 +4469,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Steelworker", rating: 3.5, num: 200, @@ -4321,6 +4482,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Steely Spirit", rating: 3.5, num: 252, @@ -4340,6 +4502,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }); } }, + flags: {}, name: "Stench", rating: 0.5, num: 1, @@ -4353,7 +4516,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Sticky Hold", rating: 1.5, num: 60, @@ -4378,7 +4541,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.effectState.target; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Storm Drain", rating: 3, num: 114, @@ -4390,6 +4553,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Strong Jaw", rating: 3.5, num: 173, @@ -4408,7 +4572,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return target.hp - 1; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Sturdy", rating: 3, num: 5, @@ -4419,7 +4583,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-activate', pokemon, 'ability: Suction Cups'); return null; }, - isBreakable: true, + flags: {breakable: 1}, name: "Suction Cups", rating: 1, num: 21, @@ -4428,6 +4592,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyCritRatio(critRatio) { return critRatio + 1; }, + flags: {}, name: "Super Luck", rating: 1.5, num: 105, @@ -4450,6 +4615,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } } }, + flags: {}, name: "Supersweet Syrup", rating: 1.5, num: 306, @@ -4474,6 +4640,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([powMod[this.effectState.fallen], 4096]); } }, + flags: {}, name: "Supreme Overlord", rating: 4, num: 293, @@ -4484,6 +4651,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Surge Surfer", rating: 3, num: 207, @@ -4503,12 +4671,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Swarm", rating: 2, num: 68, }, sweetveil: { - name: "Sweet Veil", onAllySetStatus(status, target, source, effect) { if (status.id === 'slp') { this.debug('Sweet Veil interrupts sleep'); @@ -4525,7 +4693,8 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, + name: "Sweet Veil", rating: 2, num: 175, }, @@ -4535,6 +4704,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Swift Swim", rating: 3, num: 33, @@ -4554,6 +4724,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } this.add('-activate', source, 'ability: Symbiosis', myItem, '[of] ' + pokemon); }, + flags: {}, name: "Symbiosis", rating: 0, num: 180, @@ -4568,6 +4739,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // and show messages when activating against it. source.trySetStatus(status, target, {status: status.id, id: 'synchronize'} as Effect); }, + flags: {}, name: "Synchronize", rating: 2, num: 28, @@ -4585,6 +4757,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('Sword of Ruin Def drop'); return this.chainModify(0.75); }, + flags: {}, name: "Sword of Ruin", rating: 4.5, num: 285, @@ -4602,6 +4775,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('Tablets of Ruin Atk drop'); return this.chainModify(0.75); }, + flags: {}, name: "Tablets of Ruin", rating: 4.5, num: 284, @@ -4615,7 +4789,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Tangled Feet", rating: 1, num: 77, @@ -4627,6 +4801,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({spe: -1}, source, target, null, true); } }, + flags: {}, name: "Tangling Hair", rating: 2, num: 221, @@ -4641,6 +4816,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Technician", rating: 3.5, num: 101, @@ -4652,7 +4828,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Telepathy", rating: 0, num: 140, @@ -4666,13 +4842,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.field.clearTerrain(); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1}, name: "Teraform Zero", rating: 3, num: 309, }, terashell: { onEffectiveness(typeMod, target, type, move) { - if (!target || target.species.name !== 'Terapagos-Terastal') return; + if (!target || target.baseSpecies.name !== 'Terapagos-Terastal') return; if (this.effectState.resisted) return -1; // all hits of multi-hit move should be not very effective if (move.category === 'Status') return; if (!target.runImmunity(move.type)) return; // immunity has priority @@ -4685,20 +4862,20 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onAnyAfterMove() { this.effectState.resisted = false; }, - isBreakable: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, breakable: 1}, name: "Tera Shell", rating: 3.5, num: 308, }, terashift: { onPreStart(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Terapagos' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Terapagos') return; if (pokemon.species.forme !== 'Terastal') { this.add('-activate', pokemon, 'ability: Tera Shift'); pokemon.formeChange('Terapagos-Terastal', this.effect, true); } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, name: "Tera Shift", rating: 3, num: 307, @@ -4710,6 +4887,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.ignoreAbility = true; }, + flags: {}, name: "Teravolt", rating: 3, num: 164, @@ -4733,6 +4911,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, + flags: {breakable: 1}, name: "Thermal Exchange", rating: 2.5, num: 270, @@ -4752,7 +4931,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Thick Fat", rating: 3.5, num: 47, @@ -4764,6 +4943,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(2); } }, + flags: {}, name: "Tinted Lens", rating: 4, num: 110, @@ -4783,6 +4963,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Torrent", rating: 2, num: 67, @@ -4794,6 +4975,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5325, 4096]); } }, + flags: {}, name: "Tough Claws", rating: 3.5, num: 181, @@ -4805,6 +4987,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify(1.5); } }, + flags: {}, name: "Toxic Boost", rating: 3, num: 137, @@ -4818,6 +5001,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { target.trySetStatus('tox', source); } }, + flags: {}, name: "Toxic Chain", rating: 4.5, num: 305, @@ -4831,6 +5015,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { side.addSideCondition('toxicspikes', target); } }, + flags: {}, name: "Toxic Debris", rating: 3.5, num: 295, @@ -4851,13 +5036,9 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onUpdate(pokemon) { if (!pokemon.isStarted || this.effectState.gaveUp) return; - const additionalBannedAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'noability', 'commander', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', - ]; - const possibleTargets = pokemon.adjacentFoes().filter(target => ( - !target.getAbility().isPermanent && !additionalBannedAbilities.includes(target.ability) - )); + const possibleTargets = pokemon.adjacentFoes().filter( + target => !target.getAbility().flags['notrace'] && target.ability !== 'noability' + ); if (!possibleTargets.length) return; const target = this.sample(possibleTargets); @@ -4866,6 +5047,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add('-ability', pokemon, ability, '[from] ability: Trace', '[of] ' + target); } }, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1}, name: "Trace", rating: 2.5, num: 36, @@ -4885,6 +5067,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([5325, 4096]); } }, + flags: {}, name: "Transistor", rating: 3.5, num: 262, @@ -4893,6 +5076,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyPriority(priority, pokemon, target, move) { if (move?.flags['heal']) return priority + 3; }, + flags: {}, name: "Triage", rating: 3.5, num: 205, @@ -4913,6 +5097,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.addVolatile('truant'); }, condition: {}, + flags: {}, name: "Truant", rating: -1, num: 54, @@ -4924,12 +5109,12 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { move.ignoreAbility = true; }, + flags: {}, name: "Turboblaze", rating: 3, num: 163, }, unaware: { - name: "Unaware", onAnyModifyBoost(boosts, pokemon) { const unawareUser = this.effectState.target; if (unawareUser === pokemon) return; @@ -4945,7 +5130,8 @@ export const Abilities: {[abilityid: string]: AbilityData} = { boosts['accuracy'] = 0; } }, - isBreakable: true, + flags: {breakable: 1}, + name: "Unaware", rating: 4, num: 109, }, @@ -4967,6 +5153,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } }, }, + flags: {}, name: "Unburden", rating: 3.5, num: 84, @@ -4987,6 +5174,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onFoeTryEatItem() { return !this.effectState.unnerved; }, + flags: {}, name: "Unnerve", rating: 1, num: 127, @@ -4995,6 +5183,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onModifyMove(move) { if (move.flags['contact']) delete move.flags['protect']; }, + flags: {}, name: "Unseen Fist", rating: 2, num: 260, @@ -5012,6 +5201,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.debug('Vessel of Ruin SpA drop'); return this.chainModify(0.75); }, + flags: {}, name: "Vessel of Ruin", rating: 4.5, num: 284, @@ -5023,6 +5213,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return this.chainModify([4506, 4096]); } }, + flags: {}, name: "Victory Star", rating: 2, num: 162, @@ -5047,7 +5238,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Vital Spirit", rating: 1.5, num: 72, @@ -5061,19 +5252,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Volt Absorb", rating: 3.5, num: 10, }, wanderingspirit: { onDamagingHit(damage, target, source, move) { - const additionalBannedAbilities = ['commander', 'hungerswitch', 'illusion', 'neutralizinggas', 'wonderguard']; - if (source.getAbility().isPermanent || additionalBannedAbilities.includes(source.ability) || - target.volatiles['dynamax'] - ) { - return; - } + if (source.getAbility().flags['failskillswap'] || target.volatiles['dynamax']) return; if (this.checkMoveMakesContact(move, source, target)) { const targetCanBeSet = this.runEvent('SetAbility', target, source, this.effect, source.ability); @@ -5088,6 +5274,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { target.setAbility(sourceAbility); } }, + flags: {}, name: "Wandering Spirit", rating: 2.5, num: 254, @@ -5101,7 +5288,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Water Absorb", rating: 3.5, num: 11, @@ -5142,7 +5329,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Water Bubble", rating: 4.5, num: 199, @@ -5153,6 +5340,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({def: 2}); } }, + flags: {}, name: "Water Compaction", rating: 1.5, num: 195, @@ -5171,7 +5359,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Water Veil", rating: 2, num: 41, @@ -5182,6 +5370,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({def: -1, spe: 2}, target, target); } }, + flags: {}, name: "Weak Armor", rating: 1, num: 133, @@ -5195,7 +5384,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Well-Baked Body", rating: 3.5, num: 273, @@ -5215,7 +5404,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.add("-fail", target, "unboost", "[from] ability: White Smoke", "[of] " + target); } }, - isBreakable: true, + flags: {breakable: 1}, name: "White Smoke", rating: 2, num: 73, @@ -5231,6 +5420,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { target.switchFlag = true; this.add('-activate', target, 'ability: Wimp Out'); }, + flags: {}, name: "Wimp Out", rating: 1, num: 193, @@ -5248,6 +5438,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { pokemon.addVolatile('charge'); } }, + flags: {}, name: "Wind Power", rating: 1, num: 277, @@ -5272,7 +5463,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.boost({atk: 1}, pokemon, pokemon); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Wind Rider", rating: 3.5, // We do not want Brambleghast to get Infiltrator in Randbats @@ -5292,7 +5483,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return null; } }, - isBreakable: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, failskillswap: 1, breakable: 1}, name: "Wonder Guard", rating: 5, num: 25, @@ -5305,7 +5496,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { return 50; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Wonder Skin", rating: 2, num: 147, @@ -5345,14 +5536,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } }, }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1}, name: "Zen Mode", rating: 0, num: 161, }, zerotohero: { onSwitchOut(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Palafin' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return; if (pokemon.species.forme !== 'Hero') { pokemon.formeChange('Palafin-Hero', this.effect, true); } @@ -5363,13 +5554,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(pokemon) { if (!this.effectState.switchingIn) return; this.effectState.switchingIn = false; - if (pokemon.baseSpecies.baseSpecies !== 'Palafin' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return; if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === 'Hero') { this.add('-activate', pokemon, 'ability: Zero to Hero'); this.effectState.heroMessageDisplayed = true; } }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, name: "Zero to Hero", rating: 5, num: 278, @@ -5389,14 +5580,13 @@ export const Abilities: {[abilityid: string]: AbilityData} = { } }, isNonstandard: "CAP", - isBreakable: true, + flags: {breakable: 1}, name: "Mountaineer", rating: 3, num: -2, }, rebound: { isNonstandard: "CAP", - name: "Rebound", onTryHitPriority: 1, onTryHit(target, source, move) { if (this.effectState.target.activeTurns) return; @@ -5423,14 +5613,16 @@ export const Abilities: {[abilityid: string]: AbilityData} = { condition: { duration: 1, }, - isBreakable: true, + flags: {breakable: 1}, + name: "Rebound", rating: 3, num: -3, }, persistent: { isNonstandard: "CAP", - name: "Persistent", // implemented in the corresponding move + flags: {}, + name: "Persistent", rating: 3, num: -4, }, diff --git a/data/mods/gen3/abilities.ts b/data/mods/gen3/abilities.ts index 1265649cb4d9..c1db349769cf 100644 --- a/data/mods/gen3/abilities.ts +++ b/data/mods/gen3/abilities.ts @@ -51,6 +51,10 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } }, }, + forecast: { + inherit: true, + flags: {}, + }, intimidate: { inherit: true, onStart(pokemon) { @@ -84,7 +88,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.effectState.target; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Lightning Rod", rating: 0, num: 32, @@ -167,14 +171,11 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { 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: {}, }, truant: { inherit: true, diff --git a/data/mods/gen4/abilities.ts b/data/mods/gen4/abilities.ts index f72c7dab9d3e..8fe36ce54f72 100644 --- a/data/mods/gen4/abilities.ts +++ b/data/mods/gen4/abilities.ts @@ -134,6 +134,11 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify(1.5); } }, + flags: {breakable: 1}, + }, + forecast: { + inherit: true, + flags: {notrace: 1}, }, forewarn: { inherit: true, @@ -385,7 +390,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { boosts[key]! *= 2; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Simple", rating: 4, num: 86, @@ -481,7 +486,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Thick Fat", rating: 3.5, num: 47, @@ -513,6 +518,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { this.add('-ability', pokemon, ability, '[from] ability: Trace', '[of] ' + target); } }, + flags: {notrace: 1}, }, unburden: { inherit: true, diff --git a/data/mods/gen4/moves.ts b/data/mods/gen4/moves.ts index 66566f6efd6c..919934a2655e 100644 --- a/data/mods/gen4/moves.ts +++ b/data/mods/gen4/moves.ts @@ -1321,8 +1321,7 @@ export const Moves: {[k: string]: ModdedMoveData} = { inherit: true, onTryHit(target, source) { if (target.ability === source.ability || source.hasItem('griseousorb')) return false; - const bannedTargetAbilities = ['multitype', 'wonderguard']; - if (bannedTargetAbilities.includes(target.ability) || source.ability === 'multitype') { + if (target.getAbility().flags['failroleplay'] || source.ability === 'multitype') { return false; } }, diff --git a/data/mods/gen5/abilities.ts b/data/mods/gen5/abilities.ts index ba9dd5d9268d..e85062c742a8 100644 --- a/data/mods/gen5/abilities.ts +++ b/data/mods/gen5/abilities.ts @@ -52,6 +52,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { overcoat: { inherit: true, onTryHit() {}, + flags: {}, rating: 0.5, }, sapsipper: { diff --git a/data/mods/gen6/abilities.ts b/data/mods/gen6/abilities.ts index def809f0ac31..51fc41580265 100644 --- a/data/mods/gen6/abilities.ts +++ b/data/mods/gen6/abilities.ts @@ -121,6 +121,6 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, zenmode: { inherit: true, - isPermanent: false, + flags: {failroleplay: 1, noentrain: 1, notrace: 1}, }, }; diff --git a/data/mods/gen7/abilities.ts b/data/mods/gen7/abilities.ts index 13cdad8bd6a4..c50939b0a403 100644 --- a/data/mods/gen7/abilities.ts +++ b/data/mods/gen7/abilities.ts @@ -24,11 +24,11 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, darkaura: { inherit: true, - isBreakable: true, + flags: {breakable: 1}, }, fairyaura: { inherit: true, - isBreakable: true, + flags: {breakable: 1}, }, innerfocus: { inherit: true, diff --git a/data/mods/gen7pokebilities/abilities.ts b/data/mods/gen7pokebilities/abilities.ts index 8f20a102ecac..978934e45901 100644 --- a/data/mods/gen7pokebilities/abilities.ts +++ b/data/mods/gen7pokebilities/abilities.ts @@ -4,7 +4,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { onDamagingHit(damage, target, source, move) { if (target.ability === 'mummy') { const sourceAbility = source.getAbility(); - if (sourceAbility.isPermanent || sourceAbility.id === 'mummy') { + if (sourceAbility.flags['cantsuppress'] || sourceAbility.id === 'mummy') { return; } if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { @@ -15,7 +15,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } } else { const possibleAbilities = [source.ability, ...(source.m.innates || [])] - .filter(val => !this.dex.abilities.get(val).isPermanent && val !== 'mummy'); + .filter(val => !this.dex.abilities.get(val).flags['cantsuppress'] && val !== 'mummy'); if (!possibleAbilities.length) return; if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { const abil = this.sample(possibleAbilities); @@ -43,11 +43,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const isAbility = pokemon.ability === 'powerofalchemy'; let possibleAbilities = [ally.ability]; if (ally.m.innates) possibleAbilities.push(...ally.m.innates); - const additionalBannedAbilities = [ - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) return; const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]); this.add('-ability', pokemon, ability, '[from] ability: Power of Alchemy', '[of] ' + ally); @@ -67,11 +65,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const isAbility = pokemon.ability === 'receiver'; let possibleAbilities = [ally.ability]; if (ally.m.innates) possibleAbilities.push(...ally.m.innates); - const additionalBannedAbilities = [ - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) return; const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]); this.add('-ability', pokemon, ability, '[from] ability: Receiver', '[of] ' + ally); @@ -99,12 +95,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const target = possibleTargets[rand]; let possibleAbilities = [target.ability]; if (target.m.innates) possibleAbilities.push(...target.m.innates); - const additionalBannedAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['notrace'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) { possibleTargets.splice(rand, 1); continue; diff --git a/data/mods/gen7pokebilities/scripts.ts b/data/mods/gen7pokebilities/scripts.ts index 5e9c498b30db..d97584a1c134 100644 --- a/data/mods/gen7pokebilities/scripts.ts +++ b/data/mods/gen7pokebilities/scripts.ts @@ -32,7 +32,7 @@ export const Scripts: ModdedBattleScriptsData = { ((this.volatiles['gastroacid'] || (neutralizinggas && (this.ability !== ('neutralizinggas' as ID) || this.m.innates?.some((k: string) => k === 'neutralizinggas')) - )) && !this.getAbility().isPermanent + )) && !this.getAbility().flags['cantsuppress'] ) ); }, diff --git a/data/mods/gen8/abilities.ts b/data/mods/gen8/abilities.ts index 382cbf802eff..1c97461b8129 100644 --- a/data/mods/gen8/abilities.ts +++ b/data/mods/gen8/abilities.ts @@ -371,6 +371,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, gulpmissile: { inherit: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, rating: 2.5, }, guts: { @@ -442,7 +443,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { inherit: true, onTryBoost() {}, onModifyMove() {}, - isBreakable: undefined, + flags: {}, rating: 0, }, illusion: { @@ -1163,6 +1164,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, wonderguard: { inherit: true, + flags: {failroleplay: 1, noreceiver: 1, failskillswap: 1, breakable: 1}, rating: 5, }, wonderskin: { diff --git a/data/mods/gen9dlc1/abilities.ts b/data/mods/gen9dlc1/abilities.ts index 9134fc736892..7b62af1a0c7d 100644 --- a/data/mods/gen9dlc1/abilities.ts +++ b/data/mods/gen9dlc1/abilities.ts @@ -1,4 +1,12 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { + commander: { + inherit: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1}, + }, + gulpmissile: { + inherit: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, + }, protosynthesis: { inherit: true, condition: { @@ -46,7 +54,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { this.add('-end', pokemon, 'Protosynthesis'); }, }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1, cantsuppress: 1}, }, quarkdrive: { inherit: true, @@ -95,6 +103,6 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { this.add('-end', pokemon, 'Quark Drive'); }, }, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1, cantsuppress: 1}, }, }; diff --git a/data/mods/gen9predlc/abilities.ts b/data/mods/gen9predlc/abilities.ts index 5fcff8e888e5..f2892d7d06ac 100644 --- a/data/mods/gen9predlc/abilities.ts +++ b/data/mods/gen9predlc/abilities.ts @@ -1,17 +1,21 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { commander: { inherit: true, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, + }, + gulpmissile: { + inherit: true, + flags: {cantsuppress: 1, notransform: 1}, }, hadronengine: { inherit: true, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, }, illuminate: { inherit: true, onTryBoost() {}, onModifyMove() {}, - isBreakable: undefined, + flags: {}, rating: 0, }, mindseye: { @@ -20,7 +24,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, orichalcumpulse: { inherit: true, - isPermanent: true, + flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1}, }, supersweetsyrup: { inherit: true, diff --git a/data/mods/partnersincrime/abilities.ts b/data/mods/partnersincrime/abilities.ts index 129619d44905..c8c348ad2464 100644 --- a/data/mods/partnersincrime/abilities.ts +++ b/data/mods/partnersincrime/abilities.ts @@ -19,7 +19,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { this.add('-end', target, 'Slow Start', '[silent]'); } if (target.m.innate) { - if (!this.dex.abilities.get(target.m.innate.slice(8)).isPermanent) { + if (!this.dex.abilities.get(target.m.innate.slice(8)).flags['cantsuppress']) { target.removeVolatile(target.m.innate); } } @@ -55,13 +55,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { if (!pokemon.isStarted || this.effectState.gaveUp) return; const isAbility = pokemon.ability === 'trace'; - const additionalBannedAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', - ]; - const possibleTargets = pokemon.adjacentFoes().filter(target => ( - !target.getAbility().isPermanent && !additionalBannedAbilities.includes(target.ability) - )); + const possibleTargets = pokemon.adjacentFoes().filter( + target => !target.getAbility().flags['notrace'] && target.ability !== 'noability' + ); if (!possibleTargets.length) return; const target = this.sample(possibleTargets); diff --git a/data/mods/partnersincrime/scripts.ts b/data/mods/partnersincrime/scripts.ts index 308f84c9ba9c..f167a02470e3 100644 --- a/data/mods/partnersincrime/scripts.ts +++ b/data/mods/partnersincrime/scripts.ts @@ -270,7 +270,7 @@ export const Scripts: ModdedBattleScriptsData = { if (typeof ability === 'string') ability = this.battle.dex.abilities.get(ability); const oldAbility = this.ability; if (!isFromFormeChange) { - if (ability.isPermanent || this.getAbility().isPermanent) return false; + if (ability.flags['cantsuppress'] || this.getAbility().flags['cantsuppress']) return false; } if (!this.battle.runEvent('SetAbility', this, source, this.battle.effect, ability)) return false; this.battle.singleEvent('End', this.battle.dex.abilities.get(oldAbility), this.abilityState, this, source); diff --git a/data/mods/pokebilities/abilities.ts b/data/mods/pokebilities/abilities.ts index bda83b850635..2a404d94fcf3 100644 --- a/data/mods/pokebilities/abilities.ts +++ b/data/mods/pokebilities/abilities.ts @@ -4,7 +4,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { onDamagingHit(damage, target, source, move) { if (target.ability === 'mummy') { const sourceAbility = source.getAbility(); - if (sourceAbility.isPermanent || sourceAbility.id === 'mummy') { + if (sourceAbility.flags['cantsuppress'] || sourceAbility.id === 'mummy') { return; } if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { @@ -15,7 +15,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } } else { const possibleAbilities = [source.ability, ...(source.m.innates || [])] - .filter(val => !this.dex.abilities.get(val).isPermanent && val !== 'mummy'); + .filter(val => !this.dex.abilities.get(val).flags['cantsuppress'] && val !== 'mummy'); if (!possibleAbilities.length) return; if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) { const abil = this.sample(possibleAbilities); @@ -44,7 +44,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { // Remove setter's innates before the ability starts if (pokemon.m.innates) { for (const innate of pokemon.m.innates) { - if (this.dex.abilities.get(innate).isPermanent || innate === 'neutralizinggas') continue; + if (this.dex.abilities.get(innate).flags['cantsuppress'] || innate === 'neutralizinggas') continue; pokemon.removeVolatile('ability:' + innate); } } @@ -58,7 +58,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } if (target.m.innates) { for (const innate of target.m.innates) { - if (this.dex.abilities.get(innate).isPermanent) continue; + if (this.dex.abilities.get(innate).flags['cantsuppress']) continue; target.removeVolatile('ability:' + innate); } } @@ -100,11 +100,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const isAbility = pokemon.ability === 'powerofalchemy'; let possibleAbilities = [ally.ability]; if (ally.m.innates) possibleAbilities.push(...ally.m.innates); - const additionalBannedAbilities = [ - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) return; const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]); this.add('-ability', pokemon, ability, '[from] ability: Power of Alchemy', '[of] ' + ally); @@ -124,11 +122,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const isAbility = pokemon.ability === 'receiver'; let possibleAbilities = [ally.ability]; if (ally.m.innates) possibleAbilities.push(...ally.m.innates); - const additionalBannedAbilities = [ - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) return; const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]); this.add('-ability', pokemon, ability, '[from] ability: Receiver', '[of] ' + ally); @@ -156,12 +152,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { const target = possibleTargets[rand]; let possibleAbilities = [target.ability]; if (target.m.innates) possibleAbilities.push(...target.m.innates); - const additionalBannedAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', pokemon.ability, ...(pokemon.m.innates || []), - ]; + const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])]; possibleAbilities = possibleAbilities - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['notrace'] && !additionalBannedAbilities.includes(val)); if (!possibleAbilities.length) { possibleTargets.splice(rand, 1); continue; @@ -182,13 +175,8 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { inherit: true, onDamagingHit(damage, target, source, move) { const isAbility = target.ability === 'wanderingspirit'; - const additionalBannedAbilities = ['hungerswitch', 'illusion', 'neutralizinggas', 'wonderguard']; if (isAbility) { - if (source.getAbility().isPermanent || additionalBannedAbilities.includes(source.ability) || - target.volatiles['dynamax'] - ) { - return; - } + if (source.getAbility().flags['failskillswap'] || target.volatiles['dynamax']) return; if (this.checkMoveMakesContact(move, source, target)) { const sourceAbility = source.setAbility('wanderingspirit', target); @@ -203,7 +191,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } else { // Make Wandering Spirit replace a random ability const possibleAbilities = [source.ability, ...(source.m.innates || [])] - .filter(val => !this.dex.abilities.get(val).isPermanent && !additionalBannedAbilities.includes(val)); + .filter(val => !this.dex.abilities.get(val).flags['failskillswap']); if (!possibleAbilities.length || target.volatiles['dynamax']) return; if (move.flags['contact']) { const sourceAbility = this.sample(possibleAbilities); diff --git a/data/mods/pokebilities/scripts.ts b/data/mods/pokebilities/scripts.ts index 42241f509f8c..1d6c15c64657 100644 --- a/data/mods/pokebilities/scripts.ts +++ b/data/mods/pokebilities/scripts.ts @@ -32,7 +32,7 @@ export const Scripts: ModdedBattleScriptsData = { ((this.volatiles['gastroacid'] || (neutralizinggas && (this.ability !== ('neutralizinggas' as ID) || this.m.innates?.some((k: string) => k === 'neutralizinggas')) - )) && !this.getAbility().isPermanent + )) && !this.getAbility().flags['cantsuppress'] ) ); }, diff --git a/data/mods/sharedpower/abilities.ts b/data/mods/sharedpower/abilities.ts index f715c4e0d4e8..53a0e365d961 100644 --- a/data/mods/sharedpower/abilities.ts +++ b/data/mods/sharedpower/abilities.ts @@ -16,7 +16,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } if (target.m.abils?.length) { for (const key of target.m.abils) { - if (this.dex.abilities.get(key.slice(8)).isPermanent) continue; + if (this.dex.abilities.get(key.slice(8)).flags['cantsuppress']) continue; target.removeVolatile(key); } } @@ -56,13 +56,9 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { if (!pokemon.isStarted || this.effectState.gaveUp) return; const isAbility = pokemon.ability === 'trace'; - const additionalBannedAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'noability', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', - ]; - const possibleTargets = pokemon.adjacentFoes().filter(target => ( - !target.getAbility().isPermanent && !additionalBannedAbilities.includes(target.ability) - )); + const possibleTargets = pokemon.adjacentFoes().filter( + target => !target.getAbility().flags['notrace'] && target.ability !== 'noability' + ); if (!possibleTargets.length) return; const target = this.sample(possibleTargets); diff --git a/data/mods/sharedpower/scripts.ts b/data/mods/sharedpower/scripts.ts index c6260f5e32f0..572e4d265724 100644 --- a/data/mods/sharedpower/scripts.ts +++ b/data/mods/sharedpower/scripts.ts @@ -39,7 +39,7 @@ export const Scripts: ModdedBattleScriptsData = { ((this.volatiles['gastroacid'] || (neutralizinggas && (this.ability !== ('neutralizinggas' as ID) || this.m.abils?.includes('ability:neutralizinggas')) - )) && !this.getAbility().isPermanent + )) && !this.getAbility().flags['cantsuppress'] ) ); }, diff --git a/data/mods/ssb/abilities.ts b/data/mods/ssb/abilities.ts index 0a1d97bb874f..3ab4fcfc922e 100644 --- a/data/mods/ssb/abilities.ts +++ b/data/mods/ssb/abilities.ts @@ -168,7 +168,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } } }, - isPermanent: true, + flags: {cantsuppress: 1}, onModifyMove(move) { move.ignoreAbility = true; }, @@ -193,7 +193,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { setthestage: { desc: "If this Pokemon is an Aegislash, it changes to Blade Forme before attempting to use an attacking move, and changes to Shield Forme before attempting to use King's Shield. This Pokemon's moves that match one of its types have a same-type attack bonus (STAB) of 2 instead of 1.5. On switch-in, this Pokemon selects a physical or special set.", shortDesc: "Stance Change + Adaptability; on switch-in, selects physical or special set.", - isPermanent: true, + flags: {cantsuppress: 1}, onSwitchIn(pokemon) { if (pokemon.species.baseSpecies !== 'Aegislash') return; const forme = this.randomChance(1, 2) ? 'aegii-Alt' : 'aegii'; @@ -236,7 +236,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "On switch-in, this Pokemon summons Rain Dance. If Rain Dance or Heavy Rain is active, this Pokemon has doubled Speed, collects a raindrop, and restores 1/8 of its maximum HP, rounded down, at the end of each turn. If this Pokemon is holding Big Root, it will restore 1/6 of its maximum HP, rounded down, at the end of the turn. If this Pokemon is holding Utility Umbrella, its HP does not get restored and it does not collect raindrops. Each raindrop raises this Pokemon's Defense and Special Defense by 1 stage while it is collected.", shortDesc: "Drizzle + Swift Swim. Restore HP if raining. Collect raindrops.", name: "Rainy Season", - isPermanent: true, + flags: {cantsuppress: 1}, onStart(source) { for (const action of this.queue) { if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'kyogre') return; @@ -627,7 +627,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "If this Pokemon is a Kartana, then when it switches in, it changes to two random types and gets corresponding STAB attacks.", shortDesc: "Kartana: User gains 2 random types and STAB moves on switch-in.", name: "Bipolar", - isPermanent: true, + flags: {cantsuppress: 1}, onSwitchIn(pokemon) { if (pokemon.species.baseSpecies !== 'Kartana') return; const typeMap: {[key: string]: string} = { @@ -684,7 +684,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "If this Pokemon's species is Alcremie, it alternates one of its moves between two different options at the end of each turn, depending on the forme of Alcremie.", shortDesc: "Alcremie: alternates between moves each turn.", name: "Winding Song", - isPermanent: true, + flags: {cantsuppress: 1}, onResidual(pokemon) { if (pokemon.species.baseSpecies !== 'Alcremie') return; let coolMoves = []; @@ -754,7 +754,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { name: "Armor Time", desc: "If this Pokemon uses a status move or King Giri Giri Slash, it changes its typing and boosts one of its stats by 1 stage randomly between four options: Bug/Fire type with a Special Attack boost, Bug/Steel type with a Defense boost, Bug/Rock type with a Special Defense boost, and Bug/Electric type with a Speed boost.", shortDesc: "On use of status or King Giri Giri Slash, the user changes type and gets a boost.", - isPermanent: true, + flags: {cantsuppress: 1}, onBeforeMove(source, target, move) { if (move.category !== "Status" && move.id !== "kinggirigirislash") return; const types = ['Fire', 'Steel', 'Rock', 'Electric']; @@ -829,8 +829,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "While in Minior-Meteor forme, this Pokemon cannot be affected by major status conditions and is immune to critical hits. This ability cannot be ignored by Moongeist Beam, Sunsteel Strike, Mold Breaker, Teravolt, or Turboblaze.", shortDesc: "Minior-Meteor: Immune to crits and status", name: "Capsule Armor", - isPermanent: true, - isBreakable: false, + flags: {cantsuppress: 1}, onCriticalHit: false, onSetStatus(status, target, source, effect) { if (target.species.id !== 'miniormeteor' || target.transformed) return; @@ -1678,7 +1677,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "If this Pokemon is a forme of Necrozma, its forme changes on switch-in depending on the number of unfainted Pokemon on the user's team: Necrozma-Dusk-Mane if 3 or fewer Pokemon and Necrozma-Dawn-Wings was sent out already; Necrozma-Ultra if it is the last Pokemon left on the team and Necrozma-Dusk-Mane was sent out already.", shortDesc: "Changes forme on switch-in depending on # of remaining Pokemon on user's team.", name: "The Numbers Game", - isPermanent: true, + flags: {cantsuppress: 1}, onStart(target) { if (target.baseSpecies.baseSpecies !== 'Necrozma' || target.transformed) return; if (target.side.pokemonLeft <= 3) { @@ -1832,7 +1831,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { desc: "If this Pokemon is an Aggron and is hit by a move that is not very effective, this Pokemon becomes Aggron-Mega and its Attack is boosted by 1 stage.", shortDesc: "Aggron: If hit by resisted move, Mega Evolve and gain +1 Atk.", name: "Overasked Clause", - isPermanent: true, + flags: {cantsuppress: 1}, onHit(target, source, move) { if (target.getMoveHitData(move).typeMod < 0) { if (!target.hp) return; @@ -2008,7 +2007,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { combattraining: { desc: "If this Pokemon is a Cosplay Pikachu forme, the first hit it takes in battle deals 0 neutral damage. Confusion damage also breaks the immunity.", shortDesc: "(Pikachu-Cosplay only) First hit deals 0 damage.", - isPermanent: true, + flags: {cantsuppress: 1}, onDamagePriority: 1, onDamage(damage, target, source, effect) { const cosplayFormes = [ diff --git a/data/mods/vaporemons/abilities.ts b/data/mods/vaporemons/abilities.ts index 56d23a4f6567..4f66d2b2a60a 100644 --- a/data/mods/vaporemons/abilities.ts +++ b/data/mods/vaporemons/abilities.ts @@ -9,7 +9,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { source.formeChange('Palafin-Hero', this.effect, true); } }, - isPermanent: true, + flags: {cantsuppress: 1}, name: "Zero to Hero", shortDesc: "If this Pokemon is a Palafin in Zero Form, KOing a foe has it change to Hero Form.", rating: 5, @@ -35,7 +35,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Cute Charm", shortDesc: "This Pokemon takes 50% damage from moves of its own type.", rating: 3, @@ -93,7 +93,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify([5461, 4096]); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Grass Pelt", shortDesc: "On switch-in, summons Grassy Terrain. During Grassy Terrain, Def is 1.3333x.", rating: 4.5, @@ -846,7 +846,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Steely Spirit", rating: 3.5, shortDesc: "This Pokemon's Steel power is 2x; it can't be paralyzed; Electric power against it is halved.", @@ -865,7 +865,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } } }, - isBreakable: true, + flags: {breakable: 1}, name: "Sheer Heart", shortDesc: "Special attacks have 1.3x power; stat changes to the Special Attack stat have no effect.", }, @@ -980,7 +980,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return damage / 2; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Exoskeleton", rating: 4, shortDesc: "(Mostly functional) If Bug: no Bug weaknesses. If non-Bug: +Bug resistances.", @@ -1019,7 +1019,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } return false; }, - isBreakable: true, + flags: {breakable: 1}, name: "Water Veil", rating: 2, num: 41, @@ -1036,7 +1036,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify(0.67); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Shield Dust", rating: 4, num: 19, @@ -1166,7 +1166,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.effectState.target; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Fairy Ringer", rating: 3, shortDesc: "This Pokemon draws Fairy moves to itself to raise Atk by 1; Fairy immunity.", @@ -1191,7 +1191,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return this.chainModify(0.5); } }, - isBreakable: true, + flags: {breakable: 1}, name: "Justified", rating: 3, num: 154, @@ -1428,7 +1428,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { return false; } }, - isBreakable: true, + flags: {breakable: 1}, name: "Damp", rating: 0.5, num: 6, diff --git a/data/mods/vaporemons/moves.ts b/data/mods/vaporemons/moves.ts index 12119dfbb91c..bce7b851f495 100644 --- a/data/mods/vaporemons/moves.ts +++ b/data/mods/vaporemons/moves.ts @@ -1355,11 +1355,11 @@ export const Moves: {[k: string]: ModdedMoveData} = { priority: 0, flags: {protect: 1, mirror: 1}, onHit(target) { - if (target.getAbility().isPermanent) return; + if (target.getAbility().flags['cantsuppress']) return; target.addVolatile('gastroacid'); }, onAfterSubDamage(damage, target) { - if (target.getAbility().isPermanent) return; + if (target.getAbility().flags['cantsuppress']) return; target.addVolatile('gastroacid'); }, secondary: null, diff --git a/data/mods/vaporemons/scripts.ts b/data/mods/vaporemons/scripts.ts index 29fef2fbc3d8..adff2dc10eb6 100644 --- a/data/mods/vaporemons/scripts.ts +++ b/data/mods/vaporemons/scripts.ts @@ -19,7 +19,7 @@ export const Scripts: ModdedBattleScriptsData = { return !!( (this.battle.gen >= 5 && !this.isActive) || ((this.volatiles['gastroacid'] || this.volatiles['counteract'] || - (neutralizinggas && this.ability !== ('neutralizinggas' as ID))) && !this.getAbility().isPermanent) + (neutralizinggas && this.ability !== ('neutralizinggas' as ID))) && !this.getAbility().flags['cantsuppress']) ); }, }, diff --git a/data/moves.ts b/data/moves.ts index 7e978978347a..b1cf7bab56c2 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -2936,12 +2936,12 @@ export const Moves: {[moveid: string]: MoveData} = { priority: 0, flags: {protect: 1, mirror: 1}, onHit(target) { - if (target.getAbility().isPermanent) return; + if (target.getAbility().flags['cantsuppress']) return; if (target.newlySwitched || this.queue.willMove(target)) return; target.addVolatile('gastroacid'); }, onAfterSubDamage(damage, target) { - if (target.getAbility().isPermanent) return; + if (target.getAbility().flags['cantsuppress']) return; if (target.newlySwitched || this.queue.willMove(target)) return; target.addVolatile('gastroacid'); }, @@ -3915,14 +3915,16 @@ export const Moves: {[moveid: string]: MoveData} = { flags: {}, onHit(target, source, move) { let success: boolean | null = false; - for (const pokemon of source.alliesAndSelf()) { - if (pokemon.ability === target.ability) continue; - const oldAbility = pokemon.setAbility(target.ability); - if (oldAbility) { - this.add('-ability', pokemon, target.getAbility().name, '[from] move: Doodle'); - success = true; - } else if (!success && oldAbility === null) { - success = null; + if (!target.getAbility().flags['failroleplay']) { + for (const pokemon of source.alliesAndSelf()) { + if (pokemon.ability === target.ability || pokemon.getAbility().flags['cantsuppress']) continue; + const oldAbility = pokemon.setAbility(target.ability); + if (oldAbility) { + this.add('-ability', pokemon, target.getAbility().name, '[from] move: Doodle'); + success = true; + } else if (!success && oldAbility === null) { + success = null; + } } } if (!success) { @@ -4996,15 +4998,10 @@ export const Moves: {[moveid: string]: MoveData} = { flags: {protect: 1, reflectable: 1, mirror: 1, allyanim: 1}, onTryHit(target, source) { if (target === source || target.volatiles['dynamax']) return false; - - const additionalBannedSourceAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'commander', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'zenmode', - ]; if ( target.ability === source.ability || - target.getAbility().isPermanent || target.ability === 'truant' || - source.getAbility().isPermanent || additionalBannedSourceAbilities.includes(source.ability) + target.getAbility().flags['cantsuppress'] || target.ability === 'truant' || + source.getAbility().flags['noentrain'] ) { return false; } @@ -6620,7 +6617,7 @@ export const Moves: {[moveid: string]: MoveData} = { flags: {protect: 1, reflectable: 1, mirror: 1, allyanim: 1}, volatileStatus: 'gastroacid', onTryHit(target) { - if (target.getAbility().isPermanent) { + if (target.getAbility().flags['cantsuppress']) { return false; } if (target.hasItem('Ability Shield')) { @@ -6636,7 +6633,7 @@ export const Moves: {[moveid: string]: MoveData} = { this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon, pokemon, 'gastroacid'); }, onCopy(pokemon) { - if (pokemon.getAbility().isPermanent) pokemon.removeVolatile('gastroacid'); + if (pokemon.getAbility().flags['cantsuppress']) pokemon.removeVolatile('gastroacid'); }, }, secondary: null, @@ -15856,16 +15853,7 @@ export const Moves: {[moveid: string]: MoveData} = { flags: {bypasssub: 1, allyanim: 1}, onTryHit(target, source) { if (target.ability === source.ability) return false; - - const additionalBannedTargetAbilities = [ - // Zen Mode included here for compatability with Gen 5-6 - 'commander', 'flowergift', 'forecast', 'hungerswitch', 'illusion', 'imposter', 'neutralizinggas', 'powerofalchemy', 'receiver', 'trace', 'wonderguard', 'zenmode', - ]; - - if (target.getAbility().isPermanent || additionalBannedTargetAbilities.includes(target.ability) || - source.getAbility().isPermanent) { - return false; - } + if (target.getAbility().flags['failroleplay'] || source.getAbility().flags['cantsuppress']) return false; }, onHit(target, source) { const oldAbility = source.setAbility(target.ability); @@ -17089,7 +17077,7 @@ export const Moves: {[moveid: string]: MoveData} = { priority: 0, flags: {protect: 1, reflectable: 1, mirror: 1, allyanim: 1}, onTryHit(target) { - if (target.getAbility().isPermanent || target.ability === 'simple' || target.ability === 'truant') { + if (target.getAbility().flags['cantsuppress'] || target.ability === 'simple' || target.ability === 'truant') { return false; } }, @@ -17205,15 +17193,9 @@ export const Moves: {[moveid: string]: MoveData} = { priority: 0, flags: {protect: 1, mirror: 1, bypasssub: 1, allyanim: 1}, onTryHit(target, source) { - const additionalBannedAbilities = ['hungerswitch', 'illusion', 'neutralizinggas', 'wonderguard', 'terashell']; const targetAbility = target.getAbility(); const sourceAbility = source.getAbility(); - // TODO: research in what order these should be checked - if ( - target.volatiles['dynamax'] || - targetAbility.isPermanent || sourceAbility.isPermanent || - additionalBannedAbilities.includes(target.ability) || additionalBannedAbilities.includes(source.ability) - ) { + if (sourceAbility.flags['failskillswap'] || targetAbility.flags['failskillswap'] || target.volatiles['dynamax']) { return false; } const sourceCanBeSet = this.runEvent('SetAbility', source, source, this.effect, targetAbility); @@ -21862,7 +21844,7 @@ export const Moves: {[moveid: string]: MoveData} = { } }, onTryHit(target) { - if (target.getAbility().isPermanent) { + if (target.getAbility().flags['cantsuppress']) { return false; } }, diff --git a/data/text/abilities.ts b/data/text/abilities.ts index 46b7ea50eae5..be95721de800 100644 --- a/data/text/abilities.ts +++ b/data/text/abilities.ts @@ -898,8 +898,11 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, lingeringaroma: { name: "Lingering Aroma", - desc: "Pokemon making contact with this Pokemon have their Ability changed to Lingering Aroma. Does not affect Pokemon with the As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Lingering Aroma, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, or Zero to Hero Abilities.", + desc: "Pokemon making contact with this Pokemon have their Ability changed to Lingering Aroma. Does not affect Pokemon with the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Lingering Aroma, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, or Zero to Hero Abilities.", shortDesc: "Making contact with this Pokemon has the attacker's Ability become Lingering Aroma.", + gen8: { + desc: "Pokemon making contact with this Pokemon have their Ability changed to Lingering Aroma. Does not affect Pokemon with the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Lingering Aroma, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or Zen Mode Abilities.", + }, changeAbility: " A lingering aroma clings to [TARGET]!", }, @@ -1019,8 +1022,11 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, moldbreaker: { name: "Mold Breaker", - desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Armor Tail, Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Earth Eater, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Good as Gold, Grass Pelt, Guard Dog, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Illuminate, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mind's Eye, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Purifying Salt, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Tera Shell, Thermal Exchange, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, Well-Baked Body, White Smoke, Wind Rider, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", shortDesc: "This Pokemon's moves and their effects ignore the Abilities of other Pokemon.", + gen8: { + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + }, gen7: { desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dark Aura, Dazzling, Disguise, Dry Skin, Fairy Aura, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", }, @@ -1074,7 +1080,7 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, mummy: { name: "Mummy", - desc: "Pokemon making contact with this Pokemon have their Ability changed to Mummy. Does not affect Pokemon with the As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Mummy, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, or Zero to Hero Abilities.", + desc: "Pokemon making contact with this Pokemon have their Ability changed to Mummy. Does not affect Pokemon with the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Mummy, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, or Zero to Hero Abilities.", shortDesc: "Pokemon making contact with this Pokemon have their Ability changed to Mummy.", gen8: { desc: "Pokemon making contact with this Pokemon have their Ability changed to Mummy. Does not affect Pokemon with the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Mummy, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or Zen Mode Abilities.", @@ -1109,8 +1115,11 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, neutralizinggas: { name: "Neutralizing Gas", - desc: "While this Pokemon is active, Abilities have no effect. This Ability activates before hazards and other Abilities take effect. Does not affect the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode or Zero to Hero Abilities.", + desc: "While this Pokemon is active, Abilities have no effect. This Ability activates before hazards and other Abilities take effect. Does not affect the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, or Zero to Hero Abilities.", shortDesc: "While this Pokemon is active, Abilities have no effect.", + gen8: { + desc: "While this Pokemon is active, Abilities have no effect. This Ability activates before hazards and other Abilities take effect. Does not affect the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or Zen Mode Abilities.", + }, start: " Neutralizing gas filled the area!", end: " The effects of the neutralizing gas wore off!", @@ -1285,7 +1294,7 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, powerofalchemy: { name: "Power of Alchemy", - desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, Zen Mode, and Zero to Hero.", + desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Trace, Wonder Guard, Zen Mode, and Zero to Hero.", shortDesc: "This Pokemon copies the Ability of an ally that faints.", gen8: { desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, and Zen Mode.", @@ -1427,7 +1436,7 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, receiver: { name: "Receiver", - desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, Zen Mode, and Zero to Hero.", + desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Trace, Wonder Guard, Zen Mode, and Zero to Hero.", shortDesc: "This Pokemon copies the Ability of an ally that faints.", gen8: { desc: "This Pokemon copies the Ability of an ally that faints. Abilities that cannot be copied are As One, Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, and Zen Mode.", @@ -1955,8 +1964,11 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, teravolt: { name: "Teravolt", - desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Armor Tail, Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Earth Eater, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Good as Gold, Grass Pelt, Guard Dog, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Illuminate, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mind's Eye, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Purifying Salt, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Tera Shell, Thermal Exchange, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, Well-Baked Body, White Smoke, Wind Rider, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", shortDesc: "This Pokemon's moves and their effects ignore the Abilities of other Pokemon.", + gen8: { + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + }, gen7: { desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dark Aura, Dazzling, Disguise, Dry Skin, Fairy Aura, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", }, @@ -1966,6 +1978,9 @@ export const AbilitiesText: {[k: string]: AbilityText} = { gen5: { desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Battle Armor, Big Pecks, Clear Body, Contrary, Damp, Dry Skin, Filter, Flash Fire, Flower Gift, Friend Guard, Heatproof, Heavy Metal, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Motor Drive, Multiscale, Oblivious, Own Tempo, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", }, + gen4: { + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Battle Armor, Clear Body, Damp, Dry Skin, Filter, Flash Fire, Flower Gift, Heatproof, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Lightning Rod, Limber, Magma Armor, Marvel Scale, Motor Drive, Oblivious, Own Tempo, Sand Veil, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Tangled Feet, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Veil, White Smoke, and Wonder Guard. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move. The Attack modifier from an ally's Flower Gift Ability is not negated.", + }, start: " [POKEMON] is radiating a bursting aura!", }, @@ -2020,7 +2035,7 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, trace: { name: "Trace", - desc: "On switch-in, this Pokemon copies a random opposing Pokemon's Ability. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Zen Mode, and Zero to Hero. If no opposing Pokemon has an Ability that can be copied, this Ability will activate as soon as one does.", + desc: "On switch-in, this Pokemon copies a random opposing Pokemon's Ability. Abilities that cannot be copied are As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Teraform Zero, Tera Shell, Tera Shift, Trace, Zen Mode, and Zero to Hero. If no opposing Pokemon has an Ability that can be copied, this Ability will activate as soon as one does.", shortDesc: "On switch-in, or when it can, this Pokemon copies a random adjacent foe's Ability.", gen8: { desc: "On switch-in, this Pokemon copies a random opposing Pokemon's Ability. Abilities that cannot be copied are As One, Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, and Zen Mode. If no opposing Pokemon has an Ability that can be copied, this Ability will activate as soon as one does.", @@ -2065,8 +2080,11 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, turboblaze: { name: "Turboblaze", - desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Armor Tail, Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Earth Eater, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Good as Gold, Grass Pelt, Guard Dog, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Illuminate, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mind's Eye, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Purifying Salt, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Tera Shell, Thermal Exchange, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, Well-Baked Body, White Smoke, Wind Rider, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", shortDesc: "This Pokemon's moves and their effects ignore the Abilities of other Pokemon.", + gen8: { + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dazzling, Disguise, Dry Skin, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Ice Face, Ice Scales, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Mirror Armor, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Pastel Veil, Punk Rock, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", + }, gen7: { desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Aroma Veil, Aura Break, Battle Armor, Big Pecks, Bulletproof, Clear Body, Contrary, Damp, Dark Aura, Dazzling, Disguise, Dry Skin, Fairy Aura, Filter, Flash Fire, Flower Gift, Flower Veil, Fluffy, Friend Guard, Fur Coat, Grass Pelt, Heatproof, Heavy Metal, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Motor Drive, Multiscale, Oblivious, Overcoat, Own Tempo, Queenly Majesty, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Sweet Veil, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Bubble, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", }, @@ -2076,6 +2094,9 @@ export const AbilitiesText: {[k: string]: AbilityText} = { gen5: { desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Battle Armor, Big Pecks, Clear Body, Contrary, Damp, Dry Skin, Filter, Flash Fire, Flower Gift, Friend Guard, Heatproof, Heavy Metal, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Light Metal, Lightning Rod, Limber, Magic Bounce, Magma Armor, Marvel Scale, Motor Drive, Multiscale, Oblivious, Own Tempo, Sand Veil, Sap Sipper, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Tangled Feet, Telepathy, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Veil, White Smoke, Wonder Guard, and Wonder Skin. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move, and whether or not their Ability is beneficial to this Pokemon.", }, + gen4: { + desc: "This Pokemon's moves and their effects ignore certain Abilities of other Pokemon. The Abilities that can be negated are Battle Armor, Clear Body, Damp, Dry Skin, Filter, Flash Fire, Flower Gift, Heatproof, Hyper Cutter, Immunity, Inner Focus, Insomnia, Keen Eye, Leaf Guard, Levitate, Lightning Rod, Limber, Magma Armor, Marvel Scale, Motor Drive, Oblivious, Own Tempo, Sand Veil, Shell Armor, Shield Dust, Simple, Snow Cloak, Solid Rock, Soundproof, Sticky Hold, Storm Drain, Sturdy, Suction Cups, Tangled Feet, Thick Fat, Unaware, Vital Spirit, Volt Absorb, Water Absorb, Water Veil, White Smoke, and Wonder Guard. This affects every other Pokemon on the field, whether or not it is a target of this Pokemon's move. The Attack modifier from an ally's Flower Gift Ability is not negated.", + }, start: " [POKEMON] is radiating a blazing aura!", }, @@ -2125,10 +2146,10 @@ export const AbilitiesText: {[k: string]: AbilityText} = { }, wanderingspirit: { name: "Wandering Spirit", - desc: "Pokemon making contact with this Pokemon have their Ability swapped with this one. Does not affect Pokemon with the As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, Zen Mode, or Zero to Hero Abilities.", + desc: "Pokemon making contact with this Pokemon have their Ability swapped with this one. Does not affect Pokemon with the Abilities As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Wonder Guard, Zen Mode, or Zero to Hero.", shortDesc: "Pokemon making contact with this Pokemon have their Ability swapped with this one.", gen8: { - desc: "Pokemon making contact with this Pokemon have their Ability swapped with this one. Does not affect Pokemon with the As One, Battle Bond, Comatose, Disguise, Gulp Missile, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, or Zen Mode Abilities.", + desc: "Pokemon making contact with this Pokemon have their Ability swapped with this one. Does not affect Pokemon with the Abilities As One, Battle Bond, Comatose, Disguise, Gulp Missile, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, or Zen Mode.", }, activate: "#skillswap", diff --git a/data/text/moves.ts b/data/text/moves.ts index 022695f6667f..9bb79968bf63 100644 --- a/data/text/moves.ts +++ b/data/text/moves.ts @@ -1080,7 +1080,7 @@ export const MovesText: {[k: string]: MoveText} = { }, coreenforcer: { name: "Core Enforcer", - desc: "If the user moves after the target, the target's Ability is rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, or Zero to Hero, this effect does not happen, and receiving the effect through Baton Pass ends the effect immediately.", + desc: "If the user moves after the target, the target's Ability is rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, or Zero to Hero, this effect does not happen, and receiving the effect through Baton Pass ends the effect immediately.", shortDesc: "Nullifies the foe(s) Ability if the foe(s) move first.", gen8: { desc: "If the user moves after the target, the target's Ability is rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or Zen Mode, this effect does not happen, and receiving the effect through Baton Pass ends the effect immediately.", @@ -1430,7 +1430,7 @@ export const MovesText: {[k: string]: MoveText} = { }, doodle: { name: "Doodle", - desc: "The user and its ally's Abilities change to match the target's Ability. Does not change Ability if the user's or its ally's is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, Zero to Hero, or already matches the target. Fails if both the user and its ally's Ability already matches the target, or if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, Zen Mode, or Zero to Hero.", + desc: "The user and its ally's Abilities change to match the target's Ability. Does not change Ability if the user's or its ally's is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, Zero to Hero, or already matches the target. Fails if both the user and its ally's Ability already matches the target, or if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Trace, Wonder Guard, Zen Mode, or Zero to Hero.", shortDesc: "User and ally's Abilities become target's Ability.", }, doomdesire: { @@ -1808,7 +1808,7 @@ export const MovesText: {[k: string]: MoveText} = { }, entrainment: { name: "Entrainment", - desc: "Causes the target's Ability to become the same as the user's. Fails if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Truant, Zen Mode, Zero to Hero, or the same Ability as the user, or if the user's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Zen Mode, or Zero to Hero.", + desc: "Causes the target's Ability to become the same as the user's. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Truant, Zen Mode, or Zero to Hero, or the same Ability as the user, or if the user's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Trace, Wonder Guard, Zen Mode, or Zero to Hero.", shortDesc: "The target's Ability changes to match the user's.", gen8: { desc: "Causes the target's Ability to become the same as the user's. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Truant, Zen Mode, or the same Ability as the user, or if the user's Ability is As One, Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, or Zen Mode.", @@ -2380,7 +2380,7 @@ export const MovesText: {[k: string]: MoveText} = { }, gastroacid: { name: "Gastro Acid", - desc: "Causes the target's Ability to be rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, or Zero to Hero, this move fails, and receiving the effect through Baton Pass ends the effect immediately.", + desc: "Causes the target's Ability to be rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, or Zero to Hero, this move fails, and receiving the effect through Baton Pass ends the effect immediately.", shortDesc: "Nullifies the target's Ability.", gen8: { desc: "Causes the target's Ability to be rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or Zen Mode, this move fails, and receiving the effect through Baton Pass ends the effect immediately.", @@ -5334,7 +5334,7 @@ export const MovesText: {[k: string]: MoveText} = { }, roleplay: { name: "Role Play", - desc: "The user's Ability changes to match the target's Ability. Fails if the user's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, Zero to Hero, or already matches the target, or if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Flower Gift, Forecast, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, Zen Mode, or Zero to Hero.", + desc: "The user's Ability changes to match the target's Ability. Fails if the user's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Zen Mode, Zero to Hero, or already matches the target, or if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Flower Gift, Forecast, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Power of Alchemy, Protosynthesis, Quark Drive, Receiver, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Trace, Wonder Guard, Zen Mode, or Zero to Hero.", shortDesc: "User replaces its Ability with the target's.", gen8: { desc: "The user's Ability changes to match the target's Ability. Fails if the user's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Zen Mode, or already matches the target, or if the target's Ability is As One, Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, or Zen Mode.", @@ -5716,7 +5716,7 @@ export const MovesText: {[k: string]: MoveText} = { }, simplebeam: { name: "Simple Beam", - desc: "Causes the target's Ability to become Simple. Fails if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Simple, Stance Change, Truant, Zen Mode, or Zero to Hero.", + desc: "Causes the target's Ability to become Simple. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Simple, Stance Change, Tera Shift, Truant, Zen Mode, or Zero to Hero.", shortDesc: "The target's Ability becomes Simple.", gen8: { desc: "Causes the target's Ability to become Simple. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Multitype, Power Construct, RKS System, Schooling, Shields Down, Simple, Stance Change, Truant, or Zen Mode.", @@ -5760,7 +5760,7 @@ export const MovesText: {[k: string]: MoveText} = { }, skillswap: { name: "Skill Swap", - desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, Zen Mode, or Zero to Hero.", + desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Embody Aspect, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Poison Puppeteer, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Tera Shell, Tera Shift, Teraform Zero, Wonder Guard, Zen Mode, or Zero to Hero.", shortDesc: "The user and the target trade Abilities.", gen8: { desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, or Zen Mode.", @@ -7454,7 +7454,7 @@ export const MovesText: {[k: string]: MoveText} = { }, worryseed: { name: "Worry Seed", - desc: "Causes the target's Ability to become Insomnia. Fails if the target's Ability is As One, Battle Bond, Comatose, Commander, Disguise, Gulp Missile, Hadron Engine, Ice Face, Insomnia, Multitype, Orichalcum Pulse, Power Construct, Protosynthesis, Quark Drive, RKS System, Schooling, Shields Down, Stance Change, Truant, Zen Mode, or Zero to Hero.", + desc: "Causes the target's Ability to become Insomnia. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Insomnia, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Tera Shift, Truant, Zen Mode, or Zero to Hero.", shortDesc: "The target's Ability becomes Insomnia.", gen8: { desc: "Causes the target's Ability to become Insomnia. Fails if the target's Ability is As One, Battle Bond, Comatose, Disguise, Gulp Missile, Ice Face, Insomnia, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Truant, or Zen Mode.", diff --git a/server/chat-commands/info.ts b/server/chat-commands/info.ts index 41c00fd0cebe..ea9bbdc51b7b 100644 --- a/server/chat-commands/info.ts +++ b/server/chat-commands/info.ts @@ -820,8 +820,8 @@ export const commands: Chat.ChatCommands = { details = { Gen: String(ability.gen) || 'CAP', }; - if (ability.isPermanent) details["✓ Not affected by Gastro Acid"] = ""; - if (ability.isBreakable) details["✓ Ignored by Mold Breaker"] = ""; + if (ability.flags['cantsuppress']) details["✓ Not affected by Gastro Acid"] = ""; + if (ability.flags['breakable']) details["✓ Ignored by Mold Breaker"] = ""; } break; default: diff --git a/sim/battle.ts b/sim/battle.ts index ea5203fc2e4b..906710083f59 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -779,9 +779,9 @@ export class Battle { // it's changed; call it off continue; } - if (effect.effectType === 'Ability' && effect.isBreakable !== false && + if (effect.effectType === 'Ability' && effect.flags['breakable'] && this.suppressingAbility(effectHolder as Pokemon)) { - if (effect.isBreakable) { + if (effect.flags['breakable']) { this.debug(eventid + ' handler suppressed by Mold Breaker'); continue; } diff --git a/sim/dex-abilities.ts b/sim/dex-abilities.ts index 5d17eea3105b..ed49ffd0d90a 100644 --- a/sim/dex-abilities.ts +++ b/sim/dex-abilities.ts @@ -8,6 +8,18 @@ interface AbilityEventMethods { onStart?: (this: Battle, target: Pokemon) => void; } +/* Possible Ability flags */ +interface AbilityFlags { + breakable?: 1; // Can be suppressed by Mold Breaker and related effects + cantsuppress?: 1; // Ability can't be suppressed by e.g. Gastro Acid or Neutralizing Gas + failroleplay?: 1; // Role Play fails if target has this Ability + failskillswap?: 1; // Skill Swap fails if either the user or target has this Ability + noentrain?: 1; // Entrainment fails if user has this Ability + noreceiver?: 1; // Receiver and Power of Alchemy will not activate if an ally faints with this Ability + notrace?: 1; // Trace cannot copy this Ability + notransform?: 1; // Disables the Ability if the user is Transformed +} + export interface AbilityData extends Partial, AbilityEventMethods, PokemonEventMethods { name: string; } @@ -20,9 +32,8 @@ export class Ability extends BasicEffect implements Readonly { /** Rating from -1 Detrimental to +5 Essential; see `data/abilities.ts` for details. */ readonly rating: number; readonly suppressWeather: boolean; + readonly flags: AbilityFlags; declare readonly condition?: ConditionData; - declare readonly isPermanent?: boolean; - declare readonly isBreakable?: boolean; constructor(data: AnyObject) { super(data); @@ -30,6 +41,7 @@ export class Ability extends BasicEffect implements Readonly { this.fullname = `ability: ${this.name}`; this.effectType = 'Ability'; this.suppressWeather = !!data.suppressWeather; + this.flags = data.flags || {}; this.rating = data.rating || 0; if (!this.gen) { diff --git a/sim/pokemon.ts b/sim/pokemon.ts index 8cfac7e8fc85..12f75590ac4e 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -820,7 +820,10 @@ export class Pokemon { ignoringAbility() { if (this.battle.gen >= 5 && !this.isActive) return true; - if (this.getAbility().isPermanent) return false; + + // Certain Abilities won't activate while Transformed, even if they ordinarily couldn't be suppressed (e.g. Disguise) + if (this.getAbility().flags['notransform'] && this.transformed) return true; + if (this.getAbility().flags['cantsuppress']) return false; if (this.volatiles['gastroacid']) return true; // Check if any active pokemon have the ability Neutralizing Gas @@ -1404,7 +1407,7 @@ export class Pokemon { this.ability = ''; // Don't allow Illusion to wear off } // Ogerpon's forme change doesn't override permanent abilities - if (source || !this.getAbility().isPermanent) this.setAbility(species.abilities['0'], null, true); + if (source || !this.getAbility().flags['cantsuppress']) this.setAbility(species.abilities['0'], null, true); // However, its ability does reset upon switching out this.baseAbility = toID(species.abilities['0']); } @@ -1819,7 +1822,7 @@ export class Pokemon { if (typeof ability === 'string') ability = this.battle.dex.abilities.get(ability); const oldAbility = this.ability; if (!isFromFormeChange) { - if (ability.isPermanent || this.getAbility().isPermanent) return false; + if (ability.flags['cantsuppress'] || this.getAbility().flags['cantsuppress']) return false; } if (!isFromFormeChange && !isTransform) { const setAbilityEvent: boolean | null = this.battle.runEvent('SetAbility', this, source, this.battle.effect, ability); diff --git a/test/sim/abilities/commander.js b/test/sim/abilities/commander.js index 71d16f54e9b1..54da00f4259f 100644 --- a/test/sim/abilities/commander.js +++ b/test/sim/abilities/commander.js @@ -21,7 +21,7 @@ describe('Commander', function () { assert.cantMove(() => battle.p2.choose('move swordsdance', 'move sleeptalk')); }); - it(`should not work if either Pokemon is Transformed into Dondozo/Tatsugiri`, function () { + it(`should not work if another Pokemon is Transformed into Dondozo`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'wynaut', moves: ['sleeptalk']}, {species: 'dondozo', moves: ['sleeptalk']}, @@ -35,10 +35,26 @@ describe('Commander', function () { assert.false(!!mewDondozo.volatiles['commanded']); }); - it(`should not work if Tatsugiri is Transformed, and should work if Dondozo is Transformed`, function () { + it(`should not work if another Pokemon is Transformed into Tatsugiri`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'wynaut', moves: ['sleeptalk']}, {species: 'tatsugiri', ability: 'commander', moves: ['sleeptalk']}, + ], [ + {species: 'roggenrola', moves: ['sleeptalk']}, + {species: 'sunkern', ability: 'commander', moves: ['transform']}, + {species: 'dondozo', moves: ['transform']}, + ]]); + + battle.makeChoices('auto', 'move sleeptalk, move transform 2'); + battle.makeChoices('auto', 'switch 3, move sleeptalk'); + const dondozo = battle.p2.active[0]; + assert.false(!!dondozo.volatiles['commanded'], `Transformed Sunkern into another Tatsugiri should not trigger Commander`); + }); + + it(`should work if Tatsugiri is Transformed into another Pokemon with Commander`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', moves: ['sleeptalk']}, + {species: 'sunkern', ability: 'commander', moves: ['sleeptalk']}, ], [ {species: 'roggenrola', moves: ['sleeptalk']}, {species: 'tatsugiri', ability: 'commander', moves: ['transform']}, @@ -48,11 +64,23 @@ describe('Commander', function () { battle.makeChoices('auto', 'move sleeptalk, move transform 2'); battle.makeChoices('auto', 'switch 3, move sleeptalk'); const dondozo = battle.p2.active[0]; - assert.false(!!dondozo.volatiles['commanded'], `Transformed Tatsugiri should not trigger Commander`); + assert(!!dondozo.volatiles['commanded']); + }); + + it(`should work if Dondozo is Transformed`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', moves: ['sleeptalk']}, + {species: 'diglett', moves: ['sleeptalk']}, + ], [ + {species: 'dondozo', moves: ['transform']}, + {species: 'roggenrola', moves: ['sleeptalk']}, + {species: 'tatsugiri', ability: 'commander', moves: ['sleeptalk']}, + ]]); - battle.makeChoices('auto', 'move transform 1, switch 3'); + battle.makeChoices('auto', 'move transform 2, move sleeptalk'); battle.makeChoices('auto', 'move sleeptalk, switch 3'); - assert(!!dondozo.volatiles['commanded'], `Transformed Dondozo should trigger Commander`); + const dondozo = battle.p2.active[0]; + assert(!!dondozo.volatiles['commanded']); }); it.skip(`should cause Tatsugiri to dodge all moves, including moves which normally bypass semi-invulnerability`, function () { diff --git a/test/sim/abilities/disguise.js b/test/sim/abilities/disguise.js index d4bc8fea47c9..05f587d7564c 100644 --- a/test/sim/abilities/disguise.js +++ b/test/sim/abilities/disguise.js @@ -99,4 +99,17 @@ describe('Disguise', function () { battle.makeChoices(); assert(battle.log.every(line => !line.startsWith('|-crit'))); }); + + it(`should not work while Transformed`, function () { + battle = common.createBattle([[ + {species: 'Mimikyu', ability: 'disguise', moves: ['transform']}, + ], [ + {species: 'Mimikyu', ability: 'disguise', moves: ['sleeptalk', 'aerialace']}, + ]]); + battle.makeChoices(); + battle.makeChoices('auto', 'move aerialace'); + const transformedMimikyu = battle.p1.active[0]; + assert.species(transformedMimikyu, 'Mimikyu', `Transformed Mimikyu should not have changed to Mimikyu-busted after taking damage`); + assert.false.fullHP(transformedMimikyu); + }); }); diff --git a/test/sim/abilities/iceface.js b/test/sim/abilities/iceface.js index fc5185aa6ffb..d0e4fd6bf7c9 100644 --- a/test/sim/abilities/iceface.js +++ b/test/sim/abilities/iceface.js @@ -24,8 +24,27 @@ describe('Ice Face', function () { assert.hurts(eiscue, () => battle.makeChoices()); }); - it(`should not trigger if the Pokemon was KOed`, function () { - // TODO: Make compatible with gen 9 + it(`should not work while Transformed`, function () { + battle = common.createBattle([[ + {species: 'Eiscue', ability: 'iceface', moves: ['transform']}, + {species: 'Wynaut', moves: ['sleeptalk']}, + ], [ + {species: 'Eiscue', ability: 'iceface', moves: ['sleeptalk', 'aerialace', 'hail']}, + ]]); + battle.makeChoices(); + battle.makeChoices('move aerialace', 'move aerialace'); + const transformedEiscue = battle.p1.active[0]; + assert.species(transformedEiscue, 'Eiscue', `Transformed Eiscue should not have changed to Eiscue-Noice after taking physical damage`); + assert.false.fullHP(transformedEiscue); + + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('move transform', 'auto'); + battle.makeChoices('move hail', 'auto'); + assert.species(transformedEiscue, 'Eiscue-Noice', `Transformed Eiscue should not have changed to Eiscue after hail was set`); + }); + + it(`should not trigger if the Pokemon was KOed by Max Hailstorm`, function () { battle = common.gen(8).createBattle([[ {species: 'Eiscue', level: 1, ability: 'iceface', moves: ['sleeptalk']}, ], [ diff --git a/test/sim/abilities/mummy.js b/test/sim/abilities/mummy.js index f78f88324e67..cc90defd00dd 100644 --- a/test/sim/abilities/mummy.js +++ b/test/sim/abilities/mummy.js @@ -21,7 +21,7 @@ describe('Mummy', function () { assert.equal(battle.p2.active[0].ability, 'mummy'); }); - it(`should not change isPermanent abilities`, function () { + it(`should not change abilities that can't be suppressed`, function () { battle = common.createBattle([[ {species: 'Cofagrigus', ability: 'mummy', moves: ['sleeptalk']}, ], [ diff --git a/test/sim/abilities/protosynthesis.js b/test/sim/abilities/protosynthesis.js index 37ead4fe55d3..630f8a5964d8 100644 --- a/test/sim/abilities/protosynthesis.js +++ b/test/sim/abilities/protosynthesis.js @@ -96,4 +96,17 @@ describe('Protosynthesis', function () { battle.makeChoices('move recover', 'move venoshock'); assert.bounded(tail.maxhp - tail.hp, [84, 102]); }); + + it(`should not activate while the user is Transformed`, function () { + battle = common.createBattle([[ + {species: 'Torkoal', ability: 'drought', moves: ['sleeptalk']}, + {species: 'Ditto', ability: 'imposter', moves: ['transform']}, + ], [ + {species: 'Roaring Moon', ability: 'protosynthesis', moves: ['sleeptalk']}, + ]]); + + battle.makeChoices('switch 2', 'auto'); + const dittoMoon = battle.p1.active[0]; + assert(!dittoMoon.volatiles['protosynthesis']); + }); }); diff --git a/test/sim/abilities/trace.js b/test/sim/abilities/trace.js index c089f913a385..ad6586ce075b 100644 --- a/test/sim/abilities/trace.js +++ b/test/sim/abilities/trace.js @@ -10,6 +10,38 @@ describe('Trace', function () { battle.destroy(); }); + it(`should copy the opponent's Ability`, function () { + battle = common.createBattle([[ + {species: "Ralts", ability: 'trace', moves: ['sleeptalk']}, + ], [ + {species: "Wynaut", ability: 'shadowtag', moves: ['sleeptalk']}, + ]]); + + battle.makeChoices(); + const ralts = battle.p1.active[0]; + assert.equal(ralts.ability, 'shadowtag'); + }); + + it(`should delay copying the opponent's Ability if the initial Abilities could not be copied by Trace`, function () { + battle = common.createBattle([[ + {species: "Ralts", ability: 'trace', moves: ['sleeptalk']}, + ], [ + {species: "Arceus", ability: 'multitype', moves: ['sleeptalk']}, + {species: "Aegislash", ability: 'stancechange', moves: ['sleeptalk']}, + {species: "Wynaut", ability: 'shadowtag', moves: ['sleeptalk']}, + ]]); + + battle.makeChoices(); + const ralts = battle.p1.active[0]; + assert.equal(ralts.ability, 'trace'); + + battle.makeChoices('auto', 'switch 2'); + assert.equal(ralts.ability, 'trace'); + + battle.makeChoices('auto', 'switch 3'); + assert.equal(ralts.ability, 'shadowtag'); + }); + // see research: https://www.smogon.com/forums/threads/pokemon-sun-moon-battle-mechanics-research.3586701/post-7790209 it(`should interact properly with Ability index 0 'No Ability'`, function () { // Trace stops working if it initially finds 'No Ability' diff --git a/test/sim/moves/doodle.js b/test/sim/moves/doodle.js new file mode 100644 index 000000000000..eae73c314982 --- /dev/null +++ b/test/sim/moves/doodle.js @@ -0,0 +1,52 @@ +'use strict'; + +const assert = require('./../../assert'); +const common = require('./../../common'); + +let battle; + +describe('Doodle', function () { + afterEach(function () { + battle.destroy(); + }); + + it(`should replace the Abilities of the user and its ally with the Ability of its target`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', ability: 'shadowtag', moves: ['doodle']}, + {species: 'ironhands', ability: 'quarkdrive', moves: ['sleeptalk']}, + ], [ + {species: 'clefable', ability: 'magicguard', moves: ['sleeptalk']}, + {species: 'mudkip', ability: 'torrent', moves: ['sleeptalk']}, + ]]); + battle.makeChoices('move doodle 1, auto', 'auto'); + assert.equal(battle.p1.active[0].ability, 'magicguard'); + assert.equal(battle.p1.active[1].ability, 'magicguard'); + }); + + it(`should fail against certain Abilities`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', ability: 'shadowtag', moves: ['doodle']}, + {species: 'ironhands', ability: 'quarkdrive', moves: ['sleeptalk']}, + ], [ + {species: 'fluttermane', ability: 'protosynthesis', moves: ['sleeptalk']}, + {species: 'mudkip', ability: 'torrent', moves: ['sleeptalk']}, + ]]); + battle.makeChoices('move doodle 1, auto', 'auto'); + assert.equal(battle.p1.active[0].ability, 'shadowtag'); + assert.equal(battle.p1.active[1].ability, 'quarkdrive'); + }); + + it(`should not fail if only the user has an unreplaceable Ability`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'komala', ability: 'comatose', moves: ['doodle']}, + {species: 'wynaut', ability: 'shadowtag', moves: ['swordsdance']}, + ], [ + {species: 'clefable', ability: 'magicguard', moves: ['swordsdance']}, + {species: 'mudkip', ability: 'torrent', moves: ['swordsdance']}, + ]]); + battle.makeChoices('move doodle 1, auto', 'auto'); + assert.equal(battle.p1.active[0].ability, 'comatose'); + assert.equal(battle.p1.active[1].ability, 'magicguard'); + assert.false(battle.log.some(line => line.includes('|-fail'))); + }); +});