Add November 2025 rotational ladders (#11554)
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run

This commit is contained in:
Kris Johnson 2025-11-01 20:04:38 -06:00 committed by GitHub
parent 0005dd6a40
commit 97c7a70bfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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<string>();
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&eacute;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&eacute;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&eacute;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<string>();
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&eacute;mon nicknamed after another Pok&eacute;mon get their stats buffed by 1/4 of that Pok&eacute;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&eacute;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&eacute;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&eacute;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&eacute;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&eacute;mon project is a community dedicated to exploring and understanding the competitive Pok&eacute;mon metagame by designing, creating, and playtesting new Pok&eacute;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&eacute;mon project is a community dedicated to exploring and understanding the competitive Pok&eacute;mon metagame by designing, creating, and playtesting new Pok&eacute;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',