From b27e545e94a19911652e31448bf697c6dad9b8ea Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Mon, 1 Dec 2025 23:06:26 -0700 Subject: [PATCH] Add December 2025 rotational ladders --- config/formats.ts | 488 ++++++++++++++++++++++++---------------------- 1 file changed, 250 insertions(+), 238 deletions(-) diff --git a/config/formats.ts b/config/formats.ts index cfe96a1641..eb76f33022 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -506,202 +506,32 @@ export const Formats: import('../sim/dex-formats').FormatList = [ column: 2, }, { - 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.`, + name: "[Gen 9] Convergence", + desc: `Allows all Pokémon that have identical types to share moves and abilities.`, mod: 'gen9', // searchShow: false, - ruleset: ['Standard OMs', 'Sleep Moves Clause', 'Terastal Clause'], + ruleset: ['Standard OMs', 'Sleep Clause Mod', 'Convergence Legality', 'Terastal Clause', '!Obtainable Abilities'], 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', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Meowscarada', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', - 'Palafin', 'Palkia', 'Palkia-Origin', 'Quaquaval', 'Raging Bolt', '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', 'move:Metronome', 'Shed Tail', + 'Arceus', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Darkrai', 'Deoxys-Normal', 'Deoxys-Attack', 'Deoxys-Speed', 'Dialga', 'Dialga-Origin', + 'Dondozo', 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-oh', 'Inteleon', 'Iron Bundle', 'Iron Hands', 'Koraidon', 'Kyogre', + 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lilligant-Hisui', 'Lugia', 'Lunala', 'Magearna', 'Manaphy', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', + 'Necrozma-Dusk-Mane', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', 'Primarina', 'Rayquaza', 'Regieleki', 'Regigigas', 'Reshiram', 'Shaymin-Sky', + 'Solgaleo', 'Slaking', 'Smeargle', 'Spectrier', 'Urshifu-Single-Strike', 'Urshifu-Rapid-Strike', 'Walking Wake', 'Zacian', 'Zacian-Crowned', 'Zamazenta', + 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Comatose', 'Contrary', 'Drizzle', 'Imposter', 'Moody', 'Pure Power', 'Shadow Tag', 'Speed Boost', 'Unburden', + 'Heat Rock', 'King\'s Rock', 'Light Clay', 'Razor Fang', 'Baton Pass', 'Boomburst', 'Extreme Speed', 'Last Respects', 'Population Bomb', 'Quiver Dance', + 'Rage Fist', 'Shed Tail', 'Shell Smash', 'Spore', 'Transform', ], - 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] Camomons", - desc: `Pokémon have their types set to match their first two moves.`, + name: "[Gen 9] VoltTurn Mayhem", + desc: `Every move that targets a foe causes the user to switch out after use.`, mod: 'gen9', - // searchShow: false, - ruleset: ['Standard OMs', 'Sleep Clause Mod', 'Evasion Items Clause', 'Evasion Abilities Clause', 'Terastal Clause', 'Camomons Mod'], + ruleset: ['Standard OMs', 'Sleep Moves Clause', 'VoltTurn Mayhem 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', + 'Arceus', 'Chi-Yu', 'Chien-Pao', 'Calyrex-Ice', 'Calyrex-Shadow', 'Darkrai', 'Deoxys-Attack', 'Deoxys-Normal', 'Deoxys-Speed', 'Dialga', 'Dialga-Origin', 'Dragonite', + 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Lunala', 'Magearna', + 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palafin', 'Palkia', 'Palkia-Origin', 'Rayquaza', 'Regieleki', 'Reshiram', 'Shaymin-Sky', 'Solgaleo', + 'Terapagos', 'Zacian', 'Zacian-Crowned', 'Zekrom', 'King\'s Rock', 'Razor Fang', 'Fake Out', 'Last Respects', 'Revival Blessing', ], }, @@ -1023,6 +853,21 @@ 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.`, @@ -1037,23 +882,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ 'Razor Fang', 'Baton Pass', 'Draco Meteor', 'Last Respects', 'Overheat', 'Shed Tail', ], }, - { - name: "[Gen 9] Convergence", - desc: `Allows all Pokémon that have identical types to share moves and abilities.`, - mod: 'gen9', - searchShow: false, - ruleset: ['Standard OMs', 'Sleep Clause Mod', 'Convergence Legality', 'Terastal Clause', '!Obtainable Abilities'], - banlist: [ - 'Arceus', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chi-Yu', 'Chien-Pao', 'Darkrai', 'Deoxys-Normal', 'Deoxys-Attack', 'Deoxys-Speed', 'Dialga', 'Dialga-Origin', - 'Dondozo', 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-oh', 'Inteleon', 'Iron Bundle', 'Iron Hands', 'Koraidon', 'Kyogre', - 'Kyurem-Black', 'Kyurem-White', 'Landorus-Incarnate', 'Lilligant-Hisui', 'Lugia', 'Lunala', 'Magearna', 'Manaphy', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', - 'Necrozma-Dusk-Mane', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', 'Primarina', 'Rayquaza', 'Regieleki', 'Regigigas', 'Reshiram', 'Shaymin-Sky', - 'Solgaleo', 'Slaking', 'Smeargle', 'Spectrier', 'Urshifu-Single-Strike', 'Urshifu-Rapid-Strike', 'Walking Wake', 'Zacian', 'Zacian-Crowned', 'Zamazenta', - 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Comatose', 'Contrary', 'Drizzle', 'Imposter', 'Moody', 'Pure Power', 'Shadow Tag', 'Speed Boost', 'Unburden', - 'Heat Rock', 'King\'s Rock', 'Light Clay', 'Razor Fang', 'Baton Pass', 'Boomburst', 'Extreme Speed', 'Last Respects', 'Population Bomb', 'Quiver Dance', - 'Rage Fist', 'Shed Tail', 'Shell Smash', 'Spore', 'Transform', - ], - }, { name: "[Gen 9] Cross Evolution", desc: `Give a Pokémon a Pokémon name of the next evolution stage as a nickname to inherit stat changes, typing, abilities, and moves from the next stage Pokémon.`, @@ -1263,6 +1091,190 @@ 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', 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Meowscarada', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', + 'Palafin', 'Palkia', 'Palkia-Origin', 'Quaquaval', 'Raging Bolt', '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', 'move:Metronome', '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.`, @@ -2705,7 +2717,7 @@ export const Formats: import('../sim/dex-formats').FormatList = [ name: "[Gen 9] National Dex Doubles Ubers", mod: 'gen9', gameType: 'doubles', - searchShow: false, + // searchShow: false, ruleset: ['Standard Doubles', 'NatDex Mod', '!Gravity Sleep Clause'], banlist: ['Shedinja', 'Assist'], }, @@ -2816,7 +2828,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', @@ -3635,19 +3647,26 @@ export const Formats: import('../sim/dex-formats').FormatList = [ column: 4, }, { - name: "[Gen 8] UU", - mod: 'gen8', + name: "[Gen 1] Ubers", + mod: 'gen1', // searchShow: false, - ruleset: ['[Gen 8] OU'], - banlist: ['OU', 'UUBL', 'Light Clay'], + ruleset: ['Standard'], }, { - 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', + name: "[Gen 3] Orre Colosseum", + mod: 'gen3colosseum', // searchShow: false, - ruleset: ['[Gen 8] OU', '+CAP'], - banlist: ['Crucibellite'], + gameType: 'doubles', + ruleset: [ + 'Obtainable', 'Team Preview', 'Species Clause', 'Stadium Sleep Clause', 'Freeze Clause Mod', 'Max Team Size = 6', 'VGC Timer', + 'Nickname Clause', 'Endless Battle Clause', 'Cancel Mod', 'Picked Team Size = 4', 'Exact HP Mod', 'Item Clause = 1', 'Open Team Sheets', + ], + banlist: ['Soul Dew', 'Deoxys-Defense', 'Deoxys-Attack', 'Deoxys-Speed', 'Restricted Legendary', 'Mythical'], + unbanlist: ['Latios', 'Latias', 'Wobbuffet', 'Wynaut'], + bestOfDefault: true, + onBegin() { + this.add('rule', 'Self-KO Clause: If your last Pok\u00e9mon faints to a self-KO move or effect, you will lose the battle'); + }, }, { name: "[Gen 6] VGC 2014", @@ -3783,6 +3802,13 @@ 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', @@ -3859,6 +3885,14 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['[Gen 8] PU'], banlist: ['PU', 'ZUBL', 'Damp Rock', 'Grassy Seed'], }, + { + 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 8] Battle Stadium Singles", mod: 'gen8', @@ -4803,22 +4837,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['Standard', 'One Boost Passer Clause'], banlist: ['Uber', 'Swagger'], }, - { - name: "[Gen 3] Orre Colosseum", - mod: 'gen3colosseum', - searchShow: false, - gameType: 'doubles', - ruleset: [ - 'Obtainable', 'Team Preview', 'Species Clause', 'Stadium Sleep Clause', 'Freeze Clause Mod', 'Max Team Size = 6', 'VGC Timer', - 'Nickname Clause', 'Endless Battle Clause', 'Cancel Mod', 'Picked Team Size = 4', 'Exact HP Mod', 'Item Clause = 1', 'Open Team Sheets', - ], - banlist: ['Soul Dew', 'Deoxys-Defense', 'Deoxys-Attack', 'Deoxys-Speed', 'Restricted Legendary', 'Mythical'], - unbanlist: ['Latios', 'Latias', 'Wobbuffet', 'Wynaut'], - bestOfDefault: true, - onBegin() { - this.add('rule', 'Self-KO Clause: If your last Pok\u00e9mon faints to a self-KO move or effect, you will lose the battle'); - }, - }, { name: "[Gen 3] Custom Game", mod: 'gen3', @@ -4913,12 +4931,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ battle: { trunc: Math.trunc }, ruleset: ['HP Percentage Mod', 'Cancel Mod', 'Max Team Size = 24', 'Max Move Count = 24', 'Max Level = 9999', 'Default Level = 100'], }, - { - name: "[Gen 1] Ubers", - mod: 'gen1', - searchShow: false, - ruleset: ['Standard'], - }, { name: "[Gen 1] UU", mod: 'gen1',