From 97c7a70bfd6acbf214410a25faf2fba981f4e188 Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:04:38 -0600 Subject: [PATCH] Add November 2025 rotational ladders (#11554) --- config/formats.ts | 594 +++++++++++++++++++++++----------------------- 1 file changed, 297 insertions(+), 297 deletions(-) diff --git a/config/formats.ts b/config/formats.ts index 0f9a54164d..25d7ce0959 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -506,89 +506,203 @@ export const Formats: import('../sim/dex-formats').FormatList = [ column: 2, }, { - name: "[Gen 9] Pokebilities", - desc: `Pokémon have all of their released abilities simultaneously.`, - mod: 'pokebilities', + name: "[Gen 9] Fortemons", + desc: `Put an attacking move in the item slot to have all of a Pokémon's attacks inherit its properties.`, + mod: 'gen9', // searchShow: false, - ruleset: ['Standard OMs', 'Sleep Moves Clause'], + ruleset: ['Standard OMs', 'Sleep Moves Clause', 'Terastal Clause'], banlist: [ - // Pokemon - 'Arceus', 'Annihilape', 'Archaludon', 'Basculegion', 'Basculegion-F', 'Baxcalibur', 'Braviary-Hisui', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Conkeldurr', - 'Deoxys-Normal', 'Deoxys-Attack', 'Dialga', 'Dialga-Origin', 'Espathra', 'Eternatus', 'Excadrill', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Gouging Fire', 'Groudon', - 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Miraidon', 'Mewtwo', 'Necrozma-Dusk-Mane', - 'Necrozma-Dawn-Wings', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', 'Porygon-Z', 'Rayquaza', 'Regieleki', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', - 'Solgaleo', 'Spectrier', 'Terapagos', 'Ursaluna-Bloodmoon', 'Urshifu-Single-Strike', 'Urshifu-Rapid-Strike', 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', - 'Zekrom', 'Arena Trap', 'Moody', 'Shadow Tag', 'Bright Powder', 'Damp Rock', 'Icy Rock', 'King\'s Rock', 'Razor Fang', 'Smooth Rock', 'Baton Pass', 'Shed Tail', 'Last Respects', + 'Annihilape', 'Arceus', 'Archaludon', 'Azumarill', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Cloyster', 'Comfey', 'Deoxys-Normal', 'Deoxys-Attack', + 'Dialga-Base', 'Espathra', 'Eternatus', 'Flutter Mane', 'Giratina-Altered', 'Great Tusk', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Iron Treads', 'Koraidon', 'Kyogre', + 'Kyurem-Black', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna', 'Meowscarada', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palafin', + 'Palkia', 'Palkia-Origin', 'Rayquaza', 'Reshiram', 'Samurott-Hisui', 'Shaymin-Sky', 'Skeledirge', 'Smeargle', 'Solgaleo', 'Spectrier', 'Sneasler', 'Terapagos', + 'Urshifu', 'Urshifu-Rapid-Strike', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Moody', 'Serene Grace', 'Shadow Tag', + 'Damp Rock', 'Heat Rock', 'Light Clay', 'Baton Pass', 'Beat Up', 'Fake Out', 'Last Respects', 'Shed Tail', ], - onValidateSet(set) { - const species = this.dex.species.get(set.species); - const unSeenAbilities = Object.keys(species.abilities) - .filter(key => key !== 'S' && (key !== 'H' || !species.unreleasedHidden)) - .map(key => species.abilities[key as "0" | "1" | "H" | "S"]) - .filter(ability => ability !== set.ability); - if (unSeenAbilities.length && this.toID(set.ability) !== this.toID(species.abilities['S'])) { - for (const abilityName of unSeenAbilities) { - const banReason = this.ruleTable.check('ability:' + this.toID(abilityName)); - if (banReason) { - return [`${set.name}'s ability ${abilityName} is ${banReason}.`]; + restricted: [ + 'Doom Desire', 'Dynamic Punch', 'Electro Ball', 'Explosion', 'Gyro Ball', 'Final Gambit', 'Flail', 'Flip Turn', 'Fury Cutter', 'Future Sight', 'Grass Knot', + 'Grassy Glide', 'Hard Press', 'Heavy Slam', 'Heat Crash', 'Inferno', 'Low Kick', 'Misty Explosion', 'Nuzzle', 'Power Trip', 'Reversal', 'Self-Destruct', + 'Spit Up', 'Stored Power', 'Tera Blast', 'U-turn', 'Weather Ball', 'Zap Cannon', + ], + onValidateTeam(team) { + const itemTable = new Set(); + for (const set of team) { + const forte = this.toID(set.item); + if (!forte) continue; + const move = this.dex.moves.get(forte); + if (move.exists && move.id !== 'metronome') { + if (itemTable.has(forte)) { + return [ + `You are limited to one of each move in the item slot per team.`, + `(You have more than one ${move.name}.)`, + ]; } + itemTable.add(forte); } } }, + validateSet(set, teamHas) { + const item = set.item; + const species = this.dex.species.get(set.species); + const move = this.dex.moves.get(item); + if (!move.exists || move.id === 'metronome' || move.category === 'Status') { + return this.validateSet(set, teamHas); + } + set.item = ''; + const problems = this.validateSet(set, teamHas) || []; + set.item = item; + if (this.checkCanLearn(move, species, this.allSources(species), set)) { + problems.push(`${species.name} can't learn ${move.name}.`); + } + if (set.moves.map(this.toID).includes(move.id)) { + problems.push(`Moves in the item slot can't be in the moveslots as well.`); + } + if (this.ruleTable.has(`-move:${move.id}`)) { + problems.push(`The move ${move.name} is fully banned.`); + } + const accuracyLoweringMove = + move.secondaries?.some(secondary => secondary.boosts?.accuracy && secondary.boosts?.accuracy < 0); + const flinchMove = move.secondaries?.some(secondary => secondary.volatileStatus === 'flinch'); + const freezeMove = move.secondaries?.some(secondary => secondary.status === 'frz') || move.id === 'triattack'; + if ( + this.ruleTable.isRestricted(`move:${move.id}`) || + ((accuracyLoweringMove || move.ohko || move.multihit || move.id === 'beatup' || move.flags['charge'] || + move.priority > 0 || move.damageCallback || flinchMove || freezeMove) && + !this.ruleTable.has(`+move:${move.id}`)) + ) { + problems.push(`The move ${move.name} can't be used as an item.`); + } + return problems.length ? problems : null; + }, onBegin() { for (const pokemon of this.getAllPokemon()) { - if (pokemon.ability === this.toID(pokemon.species.abilities['S'])) { - continue; - } - pokemon.m.innates = Object.keys(pokemon.species.abilities) - .filter(key => key !== 'S' && (key !== 'H' || !pokemon.species.unreleasedHidden)) - .map(key => this.toID(pokemon.species.abilities[key as "0" | "1" | "H" | "S"])) - .filter(ability => ability !== pokemon.ability); - } - }, - onBeforeSwitchIn(pokemon) { - if (pokemon.m.innates) { - for (const innate of pokemon.m.innates) { - if (pokemon.hasAbility(innate)) continue; - const effect = 'ability:' + this.toID(innate); - pokemon.volatiles[effect] = this.initEffectState({ id: effect, target: pokemon }); + const move = this.dex.getActiveMove(pokemon.set.item); + if (move.exists && move.category !== 'Status') { + pokemon.m.forte = move; + pokemon.item = 'mail' as ID; } } }, - onSwitchOut(pokemon) { - for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { - pokemon.removeVolatile(innate); + onModifyMovePriority: 1, + onModifyMove(move, pokemon, target) { + const forte: ActiveMove = pokemon.m.forte; + if (move.category !== 'Status' && forte) { + move.flags = { ...move.flags, ...forte.flags }; + if (forte.self) { + if (forte.self.onHit && move.self?.onHit) { + for (const i in forte.self) { + if (i.startsWith('onHit')) continue; + (move.self as any)[i] = (forte.self as any)[i]; + } + } else { + move.self = { ...move.self, ...forte.self }; + } + } + if (forte.selfBoost?.boosts) { + if (!move.selfBoost?.boosts) move.selfBoost = { boosts: {} }; + let boostid: BoostID; + for (boostid in forte.selfBoost.boosts) { + if (!move.selfBoost.boosts![boostid]) move.selfBoost.boosts![boostid] = 0; + move.selfBoost.boosts![boostid]! += forte.selfBoost.boosts[boostid]!; + } + } + if (forte.secondaries) { + move.secondaries = [...(move.secondaries || []), ...forte.secondaries]; + } + move.critRatio = (move.critRatio || 1) + (forte.critRatio || 1) - 1; + const VALID_PROPERTIES = [ + 'alwaysHit', 'basePowerCallback', 'breaksProtect', 'drain', 'forceSTAB', 'forceSwitch', 'hasCrashDamage', 'hasSheerForce', + 'ignoreAbility', 'ignoreAccuracy', 'ignoreDefensive', 'ignoreEvasion', 'ignoreImmunity', 'mindBlownRecoil', 'noDamageVariance', + 'ohko', 'overrideDefensivePokemon', 'overrideDefensiveStat', 'overrideOffensivePokemon', 'overrideOffensiveStat', 'pseudoWeather', + 'recoil', 'selfdestruct', 'selfSwitch', 'sleepUsable', 'smartTarget', 'stealsBoosts', 'thawsTarget', 'volatileStatus', 'willCrit', + ] as const; + for (const property of VALID_PROPERTIES) { + if (forte[property]) { + move[property] = forte[property] as any; + } + } + // Added here because onEffectiveness doesn't have an easy way to reference the source + if (forte.onEffectiveness) { + move.onEffectiveness = function (typeMod, t, type, m) { + return forte.onEffectiveness!.call(this, typeMod, t, type, m); + }; + } + forte.onModifyMove?.call(this, move, pokemon, target); } }, - onFaint(pokemon) { - for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { - const innateEffect = this.dex.conditions.get(innate) as Effect; - this.singleEvent('End', innateEffect, null, pokemon); + onModifyPriority(priority, source, target, move) { + const forte = source?.m.forte; + if (move.category !== 'Status' && forte) { + if (source.hasAbility('Triage') && forte.flags['heal']) { + return priority + (move.flags['heal'] ? 0 : 3); + } + return priority + forte.priority; } }, - onAfterMega(pokemon) { - for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { - pokemon.removeVolatile(innate); + onModifyTypePriority: 1, + onModifyType(move, pokemon, target) { + const forte = pokemon.m.forte; + if (move.category !== 'Status' && forte) { + this.singleEvent('ModifyType', forte, null, pokemon, target, move, move); } - pokemon.m.innates = undefined; + }, + onHitPriority: 1, + onHit(target, source, move) { + const forte = source.m.forte; + if (move?.category !== 'Status' && forte) { + this.singleEvent('Hit', forte, {}, target, source, move); + if (forte.self) this.singleEvent('Hit', forte.self, {}, source, source, move); + this.singleEvent('AfterHit', forte, {}, target, source, move); + } + }, + onAfterSubDamage(damage, target, source, move) { + const forte = source.m.forte; + if (move?.category !== 'Status' && forte) { + this.singleEvent('AfterSubDamage', forte, null, target, source, move, damage); + } + }, + onModifySecondaries(secondaries, target, source, move) { + if (secondaries.some(s => !!s.self)) move.selfDropped = false; + }, + onAfterMoveSecondaryPriority: 1, + onAfterMoveSecondarySelf(source, target, move) { + const forte = source.m.forte; + if (move?.category !== 'Status' && forte) { + this.singleEvent('AfterMoveSecondarySelf', forte, null, source, target, move); + } + }, + onBasePowerPriority: 1, + onBasePower(basePower, source, target, move) { + const forte = source.m.forte; + if (move.category !== 'Status' && forte?.onBasePower) { + forte.onBasePower.call(this, basePower, source, target, move); + } + }, + pokemon: { + getItem() { + const move = this.battle.dex.moves.get(this.m.forte); + if (!move.exists) return Object.getPrototypeOf(this).getItem.call(this); + return { + ...this.battle.dex.items.get('mail'), + name: move.name, id: move.id, ignoreKlutz: true, onTakeItem: false, + }; + }, }, }, { - name: "[Gen 9] Tera Override", - desc: `Any moves/items/abilities with mechanics relating to a specific type get that type replaced with the user's Tera type.`, - mod: 'teraoverride', - ruleset: ['Standard OMs', 'Evasion Abilities Clause', 'Evasion Items Clause', 'Tera Type Preview'], + name: "[Gen 9] Camomons", + desc: `Pokémon have their types set to match their first two moves.`, + mod: 'gen9', + // searchShow: false, + ruleset: ['Standard OMs', 'Sleep Clause Mod', 'Evasion Items Clause', 'Evasion Abilities Clause', 'Terastal Clause', 'Camomons Mod'], banlist: [ - 'Annihilape', 'Arceus', 'Archaludon', 'Baxcalibur', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Deoxys-Attack', 'Deoxys-Normal', 'Dialga', 'Dialga-Origin', 'Espathra', - 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Groudon', 'Hawlucha', 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', - 'Lugia', 'Lunala', 'Magearna', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Ninetales-Alola', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', - 'Rayquaza', 'Regieleki', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', 'Solgaleo', 'Spectrier', 'Terapagos', 'Ursaluna-Bloodmoon', 'Urshifu', 'Urshifu-Rapid-Strike', - 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Magnet Pull', 'Moody', 'Shadow Tag', 'Focus Band', 'King\'s Rock', 'Razor Fang', 'Quick Claw', - 'Baton Pass', 'Last Respects', 'Shed Tail', 'Weather Ball', + 'Arceus', 'Baxcalibur', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Darkrai', 'Deoxys-Normal', 'Deoxys-Attack', 'Dialga', 'Dialga-Origin', 'Dragonite', 'Drednaw', + 'Enamorus-Incarnate', 'Espathra', 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Gouging Fire', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Kommo-o', 'Koraidon', 'Kyogre', + 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Manaphy', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', + 'Palafin', 'Palkia', 'Palkia-Origin', 'Rayquaza', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', 'Solgaleo', 'Spectrier', 'Tornadus-Therian', 'Ursaluna-Bloodmoon', + 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Moody', 'Shadow Tag', 'Booster Energy', 'King\'s Rock', 'Light Clay', 'Razor Fang', + 'Baton Pass', 'Last Respects', 'Shed Tail', ], - onSwitchIn(pokemon) { - this.add('-start', pokemon, pokemon.teraType, '[silent]'); - }, }, // Other Metagames @@ -909,21 +1023,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ side.sideConditions[sideCondition.id].duration = 0; }, }, - { - name: "[Gen 9] Camomons", - desc: `Pokémon have their types set to match their first two moves.`, - mod: 'gen9', - searchShow: false, - ruleset: ['Standard OMs', 'Sleep Clause Mod', 'Evasion Items Clause', 'Evasion Abilities Clause', 'Terastal Clause', 'Camomons Mod'], - banlist: [ - 'Arceus', 'Baxcalibur', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Darkrai', 'Deoxys-Normal', 'Deoxys-Attack', 'Dialga', 'Dialga-Origin', 'Dragonite', 'Drednaw', - 'Enamorus-Incarnate', 'Espathra', 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Gouging Fire', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Kommo-o', 'Koraidon', 'Kyogre', - 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Manaphy', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', - 'Palafin', 'Palkia', 'Palkia-Origin', 'Rayquaza', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', 'Solgaleo', 'Spectrier', 'Tornadus-Therian', 'Ursaluna-Bloodmoon', - 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Moody', 'Shadow Tag', 'Booster Energy', 'King\'s Rock', 'Light Clay', 'Razor Fang', - 'Baton Pass', 'Last Respects', 'Shed Tail', - ], - }, { name: "[Gen 9] Category Swap", desc: `All Special moves become Physical, and all Physical moves become Special.`, @@ -1164,190 +1263,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ } }, }, - { - name: "[Gen 9] Fortemons", - desc: `Put an attacking move in the item slot to have all of a Pokémon's attacks inherit its properties.`, - mod: 'gen9', - searchShow: false, - ruleset: ['Standard OMs', 'Sleep Moves Clause', 'Terastal Clause'], - banlist: [ - 'Annihilape', 'Arceus', 'Archaludon', 'Azumarill', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Cloyster', 'Comfey', 'Deoxys-Normal', 'Deoxys-Attack', - 'Dialga-Base', 'Espathra', 'Eternatus', 'Flutter Mane', 'Giratina-Altered', 'Great Tusk', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Iron Treads', 'Koraidon', 'Kyogre', - 'Kyurem-Black', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna', 'Meowscarada', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palafin', - 'Palkia', 'Palkia-Origin', 'Rayquaza', 'Reshiram', 'Samurott-Hisui', 'Shaymin-Sky', 'Skeledirge', 'Smeargle', 'Solgaleo', 'Spectrier', 'Sneasler', 'Terapagos', - 'Urshifu', 'Urshifu-Rapid-Strike', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Moody', 'Serene Grace', 'Shadow Tag', - 'Damp Rock', 'Heat Rock', 'Light Clay', 'Baton Pass', 'Beat Up', 'Fake Out', 'Last Respects', 'Shed Tail', - ], - restricted: [ - 'Doom Desire', 'Dynamic Punch', 'Electro Ball', 'Explosion', 'Gyro Ball', 'Final Gambit', 'Flail', 'Flip Turn', 'Fury Cutter', 'Future Sight', 'Grass Knot', - 'Grassy Glide', 'Hard Press', 'Heavy Slam', 'Heat Crash', 'Inferno', 'Low Kick', 'Misty Explosion', 'Nuzzle', 'Power Trip', 'Reversal', 'Self-Destruct', - 'Spit Up', 'Stored Power', 'Tera Blast', 'U-turn', 'Weather Ball', 'Zap Cannon', - ], - onValidateTeam(team) { - const itemTable = new Set(); - for (const set of team) { - const forte = this.toID(set.item); - if (!forte) continue; - const move = this.dex.moves.get(forte); - if (move.exists && move.id !== 'metronome') { - if (itemTable.has(forte)) { - return [ - `You are limited to one of each move in the item slot per team.`, - `(You have more than one ${move.name}.)`, - ]; - } - itemTable.add(forte); - } - } - }, - validateSet(set, teamHas) { - const item = set.item; - const species = this.dex.species.get(set.species); - const move = this.dex.moves.get(item); - if (!move.exists || move.id === 'metronome' || move.category === 'Status') { - return this.validateSet(set, teamHas); - } - set.item = ''; - const problems = this.validateSet(set, teamHas) || []; - set.item = item; - if (this.checkCanLearn(move, species, this.allSources(species), set)) { - problems.push(`${species.name} can't learn ${move.name}.`); - } - if (set.moves.map(this.toID).includes(move.id)) { - problems.push(`Moves in the item slot can't be in the moveslots as well.`); - } - if (this.ruleTable.has(`-move:${move.id}`)) { - problems.push(`The move ${move.name} is fully banned.`); - } - const accuracyLoweringMove = - move.secondaries?.some(secondary => secondary.boosts?.accuracy && secondary.boosts?.accuracy < 0); - const flinchMove = move.secondaries?.some(secondary => secondary.volatileStatus === 'flinch'); - const freezeMove = move.secondaries?.some(secondary => secondary.status === 'frz') || move.id === 'triattack'; - if ( - this.ruleTable.isRestricted(`move:${move.id}`) || - ((accuracyLoweringMove || move.ohko || move.multihit || move.id === 'beatup' || move.flags['charge'] || - move.priority > 0 || move.damageCallback || flinchMove || freezeMove) && - !this.ruleTable.has(`+move:${move.id}`)) - ) { - problems.push(`The move ${move.name} can't be used as an item.`); - } - return problems.length ? problems : null; - }, - onBegin() { - for (const pokemon of this.getAllPokemon()) { - const move = this.dex.getActiveMove(pokemon.set.item); - if (move.exists && move.category !== 'Status') { - pokemon.m.forte = move; - pokemon.item = 'mail' as ID; - } - } - }, - onModifyMovePriority: 1, - onModifyMove(move, pokemon, target) { - const forte: ActiveMove = pokemon.m.forte; - if (move.category !== 'Status' && forte) { - move.flags = { ...move.flags, ...forte.flags }; - if (forte.self) { - if (forte.self.onHit && move.self?.onHit) { - for (const i in forte.self) { - if (i.startsWith('onHit')) continue; - (move.self as any)[i] = (forte.self as any)[i]; - } - } else { - move.self = { ...move.self, ...forte.self }; - } - } - if (forte.selfBoost?.boosts) { - if (!move.selfBoost?.boosts) move.selfBoost = { boosts: {} }; - let boostid: BoostID; - for (boostid in forte.selfBoost.boosts) { - if (!move.selfBoost.boosts![boostid]) move.selfBoost.boosts![boostid] = 0; - move.selfBoost.boosts![boostid]! += forte.selfBoost.boosts[boostid]!; - } - } - if (forte.secondaries) { - move.secondaries = [...(move.secondaries || []), ...forte.secondaries]; - } - move.critRatio = (move.critRatio || 1) + (forte.critRatio || 1) - 1; - const VALID_PROPERTIES = [ - 'alwaysHit', 'basePowerCallback', 'breaksProtect', 'drain', 'forceSTAB', 'forceSwitch', 'hasCrashDamage', 'hasSheerForce', - 'ignoreAbility', 'ignoreAccuracy', 'ignoreDefensive', 'ignoreEvasion', 'ignoreImmunity', 'mindBlownRecoil', 'noDamageVariance', - 'ohko', 'overrideDefensivePokemon', 'overrideDefensiveStat', 'overrideOffensivePokemon', 'overrideOffensiveStat', 'pseudoWeather', - 'recoil', 'selfdestruct', 'selfSwitch', 'sleepUsable', 'smartTarget', 'stealsBoosts', 'thawsTarget', 'volatileStatus', 'willCrit', - ] as const; - for (const property of VALID_PROPERTIES) { - if (forte[property]) { - move[property] = forte[property] as any; - } - } - // Added here because onEffectiveness doesn't have an easy way to reference the source - if (forte.onEffectiveness) { - move.onEffectiveness = function (typeMod, t, type, m) { - return forte.onEffectiveness!.call(this, typeMod, t, type, m); - }; - } - forte.onModifyMove?.call(this, move, pokemon, target); - } - }, - onModifyPriority(priority, source, target, move) { - const forte = source?.m.forte; - if (move.category !== 'Status' && forte) { - if (source.hasAbility('Triage') && forte.flags['heal']) { - return priority + (move.flags['heal'] ? 0 : 3); - } - return priority + forte.priority; - } - }, - onModifyTypePriority: 1, - onModifyType(move, pokemon, target) { - const forte = pokemon.m.forte; - if (move.category !== 'Status' && forte) { - this.singleEvent('ModifyType', forte, null, pokemon, target, move, move); - } - }, - onHitPriority: 1, - onHit(target, source, move) { - const forte = source.m.forte; - if (move?.category !== 'Status' && forte) { - this.singleEvent('Hit', forte, {}, target, source, move); - if (forte.self) this.singleEvent('Hit', forte.self, {}, source, source, move); - this.singleEvent('AfterHit', forte, {}, target, source, move); - } - }, - onAfterSubDamage(damage, target, source, move) { - const forte = source.m.forte; - if (move?.category !== 'Status' && forte) { - this.singleEvent('AfterSubDamage', forte, null, target, source, move, damage); - } - }, - onModifySecondaries(secondaries, target, source, move) { - if (secondaries.some(s => !!s.self)) move.selfDropped = false; - }, - onAfterMoveSecondaryPriority: 1, - onAfterMoveSecondarySelf(source, target, move) { - const forte = source.m.forte; - if (move?.category !== 'Status' && forte) { - this.singleEvent('AfterMoveSecondarySelf', forte, null, source, target, move); - } - }, - onBasePowerPriority: 1, - onBasePower(basePower, source, target, move) { - const forte = source.m.forte; - if (move.category !== 'Status' && forte?.onBasePower) { - forte.onBasePower.call(this, basePower, source, target, move); - } - }, - pokemon: { - getItem() { - const move = this.battle.dex.moves.get(this.m.forte); - if (!move.exists) return Object.getPrototypeOf(this).getItem.call(this); - return { - ...this.battle.dex.items.get('mail'), - name: move.name, id: move.id, ignoreKlutz: true, onTakeItem: false, - }; - }, - }, - }, { name: "[Gen 9] Frantic Fusions", desc: `Pokémon nicknamed after another Pokémon get their stats buffed by 1/4 of that Pokémon's stats, barring HP, and access to one of their abilities.`, @@ -1716,6 +1631,73 @@ export const Formats: import('../sim/dex-formats').FormatList = [ 'Speed Boost', 'Heat Rock', 'King\'s Rock', 'Razor Fang', 'Quick Claw', 'Baton Pass', 'Last Respects', 'Shed Tail', ], }, + { + name: "[Gen 9] Pokebilities", + desc: `Pokémon have all of their released abilities simultaneously.`, + mod: 'pokebilities', + searchShow: false, + ruleset: ['Standard OMs', 'Sleep Moves Clause'], + banlist: [ + 'Arceus', 'Annihilape', 'Archaludon', 'Basculegion', 'Basculegion-F', 'Baxcalibur', 'Braviary-Hisui', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Conkeldurr', + 'Deoxys-Normal', 'Deoxys-Attack', 'Dialga', 'Dialga-Origin', 'Espathra', 'Eternatus', 'Excadrill', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Gouging Fire', 'Groudon', + 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Miraidon', 'Mewtwo', 'Necrozma-Dusk-Mane', + 'Necrozma-Dawn-Wings', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', 'Porygon-Z', 'Rayquaza', 'Regieleki', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', + 'Solgaleo', 'Spectrier', 'Terapagos', 'Ursaluna-Bloodmoon', 'Urshifu-Single-Strike', 'Urshifu-Rapid-Strike', 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', + 'Zekrom', 'Arena Trap', 'Moody', 'Shadow Tag', 'Bright Powder', 'Damp Rock', 'Icy Rock', 'King\'s Rock', 'Razor Fang', 'Smooth Rock', 'Baton Pass', 'Shed Tail', 'Last Respects', + ], + onValidateSet(set) { + const species = this.dex.species.get(set.species); + const unSeenAbilities = Object.keys(species.abilities) + .filter(key => key !== 'S' && (key !== 'H' || !species.unreleasedHidden)) + .map(key => species.abilities[key as "0" | "1" | "H" | "S"]) + .filter(ability => ability !== set.ability); + if (unSeenAbilities.length && this.toID(set.ability) !== this.toID(species.abilities['S'])) { + for (const abilityName of unSeenAbilities) { + const banReason = this.ruleTable.check('ability:' + this.toID(abilityName)); + if (banReason) { + return [`${set.name}'s ability ${abilityName} is ${banReason}.`]; + } + } + } + }, + onBegin() { + for (const pokemon of this.getAllPokemon()) { + if (pokemon.ability === this.toID(pokemon.species.abilities['S'])) { + continue; + } + pokemon.m.innates = Object.keys(pokemon.species.abilities) + .filter(key => key !== 'S' && (key !== 'H' || !pokemon.species.unreleasedHidden)) + .map(key => this.toID(pokemon.species.abilities[key as "0" | "1" | "H" | "S"])) + .filter(ability => ability !== pokemon.ability); + } + }, + onBeforeSwitchIn(pokemon) { + if (pokemon.m.innates) { + for (const innate of pokemon.m.innates) { + if (pokemon.hasAbility(innate)) continue; + const effect = 'ability:' + this.toID(innate); + pokemon.volatiles[effect] = this.initEffectState({ id: effect, target: pokemon }); + } + } + }, + onSwitchOut(pokemon) { + for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { + pokemon.removeVolatile(innate); + } + }, + onFaint(pokemon) { + for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { + const innateEffect = this.dex.conditions.get(innate) as Effect; + this.singleEvent('End', innateEffect, null, pokemon); + } + }, + onAfterMega(pokemon) { + for (const innate of Object.keys(pokemon.volatiles).filter(i => i.startsWith('ability:'))) { + pokemon.removeVolatile(innate); + } + pokemon.m.innates = undefined; + }, + }, { name: "[Gen 9] Pokemoves", desc: `Put a Pokémon's name in a moveslot to turn them into a move. The move has 8 PP, 100% accuracy, and a category and Base Power matching their highest attacking stat. Use /pokemove for more info.`, @@ -2232,6 +2214,24 @@ export const Formats: import('../sim/dex-formats').FormatList = [ }, }, }, + { + name: "[Gen 9] Tera Override", + desc: `Any moves/items/abilities with mechanics relating to a specific type get that type replaced with the user's Tera type.`, + mod: 'teraoverride', + searchShow: false, + ruleset: ['Standard OMs', 'Evasion Abilities Clause', 'Evasion Items Clause', 'Tera Type Preview'], + banlist: [ + 'Annihilape', 'Arceus', 'Archaludon', 'Baxcalibur', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Deoxys-Attack', 'Deoxys-Normal', 'Dialga', 'Dialga-Origin', 'Espathra', + 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Groudon', 'Hawlucha', 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', + 'Lugia', 'Lunala', 'Magearna', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Ninetales-Alola', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', + 'Rayquaza', 'Regieleki', 'Reshiram', 'Roaring Moon', 'Shaymin-Sky', 'Sneasler', 'Solgaleo', 'Spectrier', 'Terapagos', 'Ursaluna-Bloodmoon', 'Urshifu', 'Urshifu-Rapid-Strike', + 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Magnet Pull', 'Moody', 'Shadow Tag', 'Focus Band', 'King\'s Rock', 'Razor Fang', 'Quick Claw', + 'Baton Pass', 'Last Respects', 'Shed Tail', 'Weather Ball', + ], + onSwitchIn(pokemon) { + this.add('-start', pokemon, pokemon.teraType, '[silent]'); + }, + }, { name: "[Gen 9] The Card Game", desc: `The type chart is simplified based off of the Pokémon Trading Card Game.`, @@ -2607,7 +2607,7 @@ export const Formats: import('../sim/dex-formats').FormatList = [ name: "[Gen 9] National Dex 35 Pokes", desc: `Only 35 Pokémon are legal.`, mod: 'gen9', - // searchShow: false, + searchShow: false, ruleset: [ 'Standard NatDex', '!Species Clause', 'Forme Clause', 'Terastal Clause', 'DryPass Clause', 'Z-Move Clause', 'Mega Rayquaza Clause', @@ -2813,7 +2813,7 @@ export const Formats: import('../sim/dex-formats').FormatList = [ { name: "[Gen 9] National Dex STABmons", mod: 'gen9', - searchShow: false, + // searchShow: false, ruleset: ['Standard NatDex', 'STABmons Move Legality', '!Sleep Clause Mod', 'Sleep Moves Clause', 'Terastal Clause'], banlist: [ 'Araquanid', 'Arceus', 'Azumarill', 'Baxcalibur', 'Blastoise-Mega', 'Blaziken-Mega', 'Basculegion', 'Basculegion-F', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', @@ -3681,28 +3681,27 @@ export const Formats: import('../sim/dex-formats').FormatList = [ column: 4, }, { - name: "[Gen 7] UU", - mod: 'gen7', + name: "[Gen 8] UU", + mod: 'gen8', // searchShow: false, - ruleset: ['[Gen 7] OU'], - banlist: ['OU', 'UUBL', 'Drizzle', 'Drought', 'Kommonium Z', 'Mewnium Z'], + ruleset: ['[Gen 8] OU'], + banlist: ['OU', 'UUBL', 'Light Clay'], }, { - name: "[Gen 5] VGC 2013", - mod: 'gen5', + name: "[Gen 8] CAP", + desc: "The Create-A-Pokémon project is a community dedicated to exploring and understanding the competitive Pokémon metagame by designing, creating, and playtesting new Pokémon concepts.", + mod: 'gen8', + // searchShow: false, + ruleset: ['[Gen 8] OU', '+CAP'], + banlist: ['Crucibellite'], + }, + { + name: "[Gen 6] VGC 2014", + mod: 'gen6xy', gameType: 'doubles', // searchShow: false, bestOfDefault: true, - ruleset: ['Flat Rules'], - banlist: ['Chatot', 'Dark Void', 'Sky Drop', 'Soul Dew'], - }, - { - name: "[Gen 8] ZU", - desc: `The unofficial usage-based tier below PU.`, - mod: 'gen8', - // searchShow: false, - ruleset: ['[Gen 8] PU'], - banlist: ['PU', 'ZUBL', 'Damp Rock', 'Grassy Seed'], + ruleset: ['Flat Rules', 'Kalos Pokedex', 'Min Source Gen = 6'], }, // Past Gens OU @@ -3830,13 +3829,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['Standard', 'Dynamax Clause'], banlist: ['AG', 'Shadow Tag', 'Baton Pass'], }, - { - name: "[Gen 8] UU", - mod: 'gen8', - searchShow: false, - ruleset: ['[Gen 8] OU'], - banlist: ['OU', 'UUBL', 'Light Clay'], - }, { name: "[Gen 8] RU", mod: 'gen8', @@ -3906,12 +3898,12 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['Obtainable', 'Team Preview', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause'], }, { - name: "[Gen 8] CAP", - desc: "The Create-A-Pokémon project is a community dedicated to exploring and understanding the competitive Pokémon metagame by designing, creating, and playtesting new Pokémon concepts.", + name: "[Gen 8] ZU", + desc: `The unofficial usage-based tier below PU.`, mod: 'gen8', searchShow: false, - ruleset: ['[Gen 8] OU', '+CAP'], - banlist: ['Crucibellite'], + ruleset: ['[Gen 8] PU'], + banlist: ['PU', 'ZUBL', 'Damp Rock', 'Grassy Seed'], }, { name: "[Gen 8] Battle Stadium Singles", @@ -4032,6 +4024,13 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['Standard', 'Mega Rayquaza Clause'], banlist: ['Baton Pass'], }, + { + name: "[Gen 7] UU", + mod: 'gen7', + searchShow: false, + ruleset: ['[Gen 7] OU'], + banlist: ['OU', 'UUBL', 'Drizzle', 'Drought', 'Kommonium Z', 'Mewnium Z'], + }, { name: "[Gen 7] RU", mod: 'gen7', @@ -4362,14 +4361,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['Flat Rules', 'Min Source Gen = 6'], banlist: ['Soul Dew', 'Articuno + Snow Cloak', 'Zapdos + Static', 'Moltres + Flame Body', 'Dragonite + Barrier'], }, - { - name: "[Gen 6] VGC 2014", - mod: 'gen6xy', - gameType: 'doubles', - searchShow: false, - bestOfDefault: true, - ruleset: ['Flat Rules', 'Kalos Pokedex', 'Min Source Gen = 6'], - }, { name: "[Gen 6] Battle Spot Doubles", mod: 'gen6', @@ -4542,6 +4533,15 @@ export const Formats: import('../sim/dex-formats').FormatList = [ section: "B2/W2 Doubles", column: 4, }, + { + name: "[Gen 5] VGC 2013", + mod: 'gen5', + gameType: 'doubles', + searchShow: false, + bestOfDefault: true, + ruleset: ['Flat Rules'], + banlist: ['Chatot', 'Dark Void', 'Sky Drop', 'Soul Dew'], + }, { name: "[Gen 5] VGC 2012", mod: 'gen5bw1',