@pkmn/sim 0.5.7 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/build/config/formats.js +442 -247
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/aliases.js +0 -2
  4. package/build/data/aliases.js.map +1 -1
  5. package/build/data/formats-data.js +3 -3
  6. package/build/data/formats-data.js.map +1 -1
  7. package/build/data/items.js +2 -2
  8. package/build/data/items.js.map +1 -1
  9. package/build/data/learnsets.js +5 -6
  10. package/build/data/learnsets.js.map +1 -1
  11. package/build/data/mods/gen2/moves.js +13 -1
  12. package/build/data/mods/gen2/moves.js.map +1 -1
  13. package/build/data/mods/gen3/formats-data.js +2 -2
  14. package/build/data/mods/gen3/formats-data.js.map +1 -1
  15. package/build/data/mods/gen4/formats-data.js +4 -4
  16. package/build/data/mods/gen4/formats-data.js.map +1 -1
  17. package/build/data/mods/gen4/moves.js +7 -5
  18. package/build/data/mods/gen4/moves.js.map +1 -1
  19. package/build/data/mods/gen5/moves.js +6 -4
  20. package/build/data/mods/gen5/moves.js.map +1 -1
  21. package/build/data/mods/gen6/items.js +2 -2
  22. package/build/data/mods/gen6/items.js.map +1 -1
  23. package/build/data/mods/gen7/moves.js +3 -3
  24. package/build/data/mods/gen7/moves.js.map +1 -1
  25. package/build/data/mods/gen7/pokedex.js +5 -0
  26. package/build/data/mods/gen7/pokedex.js.map +1 -1
  27. package/build/data/moves.js +6 -5
  28. package/build/data/moves.js.map +1 -1
  29. package/build/data/pokedex.js +3 -3
  30. package/build/data/pokedex.js.map +1 -1
  31. package/build/data/rulesets.js +23 -139
  32. package/build/data/rulesets.js.map +1 -1
  33. package/build/lib/index.js +5 -1
  34. package/build/lib/index.js.map +1 -1
  35. package/build/lib/streams.js +5 -1
  36. package/build/lib/streams.js.map +1 -1
  37. package/build/sim/dex.js +5 -1
  38. package/build/sim/dex.js.map +1 -1
  39. package/build/sim/index.js +5 -1
  40. package/build/sim/index.js.map +1 -1
  41. package/build/sim/team-validator.js +11 -0
  42. package/build/sim/team-validator.js.map +1 -1
  43. package/build/sim/teams.js +5 -1
  44. package/build/sim/teams.js.map +1 -1
  45. package/build/sim/tools/index.js +5 -1
  46. package/build/sim/tools/index.js.map +1 -1
  47. package/build/sim/tools/runner.js +5 -1
  48. package/build/sim/tools/runner.js.map +1 -1
  49. package/config/formats.ts +424 -243
  50. package/data/aliases.ts +0 -2
  51. package/data/formats-data.ts +3 -3
  52. package/data/items.ts +2 -2
  53. package/data/learnsets.ts +5 -6
  54. package/data/mods/gen2/moves.ts +13 -1
  55. package/data/mods/gen3/formats-data.ts +2 -2
  56. package/data/mods/gen4/formats-data.ts +4 -4
  57. package/data/mods/gen4/moves.ts +6 -4
  58. package/data/mods/gen5/moves.ts +6 -4
  59. package/data/mods/gen6/items.ts +2 -2
  60. package/data/mods/gen7/moves.ts +3 -3
  61. package/data/mods/gen7/pokedex.ts +5 -0
  62. package/data/moves.ts +6 -5
  63. package/data/pokedex.ts +3 -3
  64. package/data/rulesets.ts +21 -125
  65. package/package.json +2 -2
  66. package/sim/team-validator.ts +13 -0
package/config/formats.ts CHANGED
@@ -234,8 +234,8 @@ export const Formats: FormatList = [
234
234
  mod: 'gen8',
235
235
  ruleset: ['[Gen 8] PU'],
236
236
  banlist: [
237
- 'PU', 'Aurorus', 'Centiskorch', 'Drampa', 'Exeggutor-Alola', 'Gallade', 'Haunter', 'Magmortar', 'Magneton', 'Omastar', 'Rotom-Frost',
238
- 'Turtonator', 'Vanilluxe', 'Vikavolt', 'Silvally-Dragon', 'Silvally-Ground', 'Sneasel', 'Damp Rock', 'Grassy Seed',
237
+ 'PU', 'Arctovish', 'Aurorus', 'Centiskorch', 'Drampa', 'Exeggutor-Alola', 'Gallade', 'Haunter', 'Magmortar', 'Magneton', 'Omastar',
238
+ 'Rotom-Frost', 'Turtonator', 'Vanilluxe', 'Vikavolt', 'Silvally-Dragon', 'Silvally-Ground', 'Sneasel', 'Damp Rock', 'Grassy Seed',
239
239
  ],
240
240
  },
241
241
  {
@@ -362,6 +362,11 @@ export const Formats: FormatList = [
362
362
  },
363
363
  {
364
364
  name: "[Gen 8] VGC 2022",
365
+ threads: [
366
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3677186/">VGC 2022 Metagame Discussion</a>`,
367
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3695848/">VGC 2022 Sample Teams</a>`,
368
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3696395/">VGC 2022 Viability Rankings</a>`,
369
+ ],
365
370
 
366
371
  mod: 'gen8',
367
372
  gameType: 'doubles',
@@ -417,7 +422,7 @@ export const Formats: FormatList = [
417
422
  'HP Percentage Mod', 'Cancel Mod',
418
423
  ],
419
424
  banlist: [
420
- 'Pokestar Spirit', 'Shedinja + Sturdy', 'Battle Bond', 'Cheek Pouch', 'Cursed Body', 'Dry Skin', 'Fluffy', 'Fur Coat', 'Gorilla Tactics',
425
+ 'Pokestar Spirit', 'Shedinja + Sturdy', 'Battle Bond', 'Cheek Pouch', 'Cursed Body', 'Dry Skin', 'Fur Coat', 'Gorilla Tactics',
421
426
  'Grassy Surge', 'Huge Power', 'Ice Body', 'Iron Barbs', 'Libero', 'Moody', 'Neutralizing Gas', 'Parental Bond', 'Perish Body', 'Poison Heal',
422
427
  'Power Construct', 'Pressure', 'Protean', 'Pure Power', 'Rain Dish', 'Rough Skin', 'Sand Spit', 'Sand Stream', 'Snow Warning', 'Stamina',
423
428
  'Volt Absorb', 'Water Absorb', 'Wonder Guard', 'Abomasite', 'Aguav Berry', 'Assault Vest', 'Berry', 'Berry Juice', 'Berserk Gene',
@@ -476,11 +481,11 @@ export const Formats: FormatList = [
476
481
  ruleset: ['Standard NatDex', 'OHKO Clause', 'Evasion Moves Clause', 'Species Clause', 'Dynamax Clause', 'Sleep Clause Mod'],
477
482
  banlist: [
478
483
  'Alakazam-Mega', 'Arceus', 'Blastoise-Mega', 'Blaziken-Mega', 'Calyrex-Ice', 'Calyrex-Shadow', 'Cinderace', 'Darkrai',
479
- 'Darmanitan-Galar', 'Deoxys-Attack', 'Deoxys-Base', 'Deoxys-Speed', 'Dialga', 'Dracovish', 'Dragapult', 'Eternatus',
484
+ 'Darmanitan-Galar', 'Deoxys-Attack', 'Deoxys-Base', 'Deoxys-Speed', 'Dialga', 'Dracovish', 'Eternatus',
480
485
  'Genesect', 'Gengar-Mega', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kangaskhan-Mega', 'Kyogre', 'Kyurem-Black',
481
486
  'Kyurem-White', 'Landorus-Base', 'Lucario-Mega', 'Lugia', 'Lunala', 'Magearna', 'Marshadow', 'Metagross-Mega', 'Mewtwo',
482
487
  'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Necrozma-Ultra', 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram',
483
- 'Salamence-Mega', 'Shaymin-Sky', 'Solgaleo', 'Spectrier', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian',
488
+ 'Salamence-Mega', 'Shaymin-Sky', 'Solgaleo', 'Spectrier', 'Tornadus-Therian', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian',
484
489
  'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Zygarde-Base', 'Zygarde-Complete', 'Arena Trap', 'Moody',
485
490
  'Power Construct', 'Sand Veil', 'Shadow Tag', 'Snow Cloak', 'Bright Powder', 'King\'s Rock', 'Lax Incense', 'Razor Fang',
486
491
  'Quick Claw', 'Baton Pass',
@@ -506,17 +511,16 @@ export const Formats: FormatList = [
506
511
  'Drizzle', 'Drought',
507
512
  // Slowbronite is banned so it doesn't validate on Galarian Slowbro
508
513
  'Slowbronite',
509
- // NatDex suspect
510
- 'Tornadus-Therian',
514
+ // NDOU suspect
515
+ 'Dragapult',
511
516
  ],
512
517
  // Used to distinguish UU from below UU in the client
513
518
  restricted: [
514
- 'Aegislash', 'Alakazam-Base', 'Altaria-Mega', 'Amoonguss', 'Beedrill-Mega', 'Blaziken-Base', 'Breloom', 'Celesteela',
515
- 'Chandelure', 'Donphan', 'Dracozolt', 'Dragonite', 'Feraligatr', 'Gastrodon', 'Hatterene', 'Hippowdon', 'Infernape',
516
- 'Keldeo', 'Krookodile', 'Mamoswine', 'Mandibuzz', 'Manectric-Mega', 'Melmetal', 'Mienshao', 'Moltres-Base',
517
- 'Nidoking', 'Nidoqueen', 'Nihilego', 'Quagsire', 'Regieleki', 'Ribombee', 'Rotom-Heat', 'Rotom-Wash', 'Salamence',
518
- 'Scizor', 'Skarmory', 'Slowking-Base', 'Swampert-Base', 'Talonflame', 'Tangrowth', 'Tornadus-Base', 'Umbreon',
519
- 'Urshifu-Rapid-Strike',
519
+ 'Aegislash', 'Alakazam-Base', 'Altaria-Mega', 'Amoonguss', 'Azumarill', 'Beedrill-Mega', 'Blaziken-Base', 'Breloom', 'Celesteela',
520
+ 'Chandelure', 'Donphan', 'Dracozolt', 'Dragonite', 'Feraligatr', 'Gastrodon', 'Hatterene', 'Hippowdon', 'Infernape', 'Keldeo',
521
+ 'Krookodile', 'Mamoswine', 'Mandibuzz', 'Manectric-Mega', 'Melmetal', 'Mienshao', 'Moltres-Base', 'Nidoking', 'Nidoqueen', 'Nihilego',
522
+ 'Quagsire', 'Regieleki', 'Ribombee', 'Rotom-Heat', 'Rotom-Wash', 'Salamence', 'Scizor', 'Skarmory', 'Slowking-Base', 'Swampert-Base',
523
+ 'Talonflame', 'Tangrowth', 'Tornadus-Base', 'Umbreon', 'Urshifu-Rapid-Strike',
520
524
  ],
521
525
  },
522
526
  {
@@ -635,135 +639,196 @@ export const Formats: FormatList = [
635
639
  column: 2,
636
640
  },
637
641
  {
638
- name: "[Gen 8] Trademarked",
639
- desc: `Sacrifice your Pok&eacute;mon's ability for a status move that activates on switch-in.`,
642
+ name: "[Gen 8] Shared Power",
643
+ desc: `Once a Pok&eacute;mon switches in, its ability is shared with the rest of the team.`,
640
644
  threads: [
641
- `&bullet; <a href="https://www.smogon.com/forums/threads/3656980/">Trademarked</a>`,
645
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3660877/">Shared Power</a>`,
642
646
  ],
643
647
 
644
- mod: 'gen8',
648
+ mod: 'sharedpower',
645
649
  // searchShow: false,
646
650
  ruleset: ['Standard', 'Dynamax Clause'],
647
651
  banlist: [
648
- 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragapult', 'Eternatus', 'Kyurem-Black', 'Kyurem-White',
649
- 'Giratina', 'Giratina-Origin', 'Genesect', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Lugia', 'Lunala', 'Magearna', 'Marowak-Alola',
650
- 'Marshadow', 'Melmetal', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza',
651
- 'Reshiram', 'Solgaleo', 'Spectrier', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned',
652
- 'Zekrom', 'Zygarde-Base', 'Arena Trap', 'Moody', 'Neutralizing Gas', 'Power Construct', 'Shadow Tag', 'Baton Pass',
652
+ 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina',
653
+ 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna',
654
+ 'Marshadow', 'Melmetal', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia',
655
+ 'Pheromosa', 'Rayquaza', 'Reshiram', 'Shedinja', 'Solgaleo', 'Urshifu-Base', 'Urshifu-Rapid-Strike',
656
+ 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom',
657
+ 'Arena Trap', 'Contrary', 'Drizzle ++ Swift Swim', 'Drought ++ Chlorophyll', 'Electric Surge ++ Surge Surfer',
658
+ 'Fur Coat', 'Guts', 'Harvest', 'Huge Power', 'Imposter', 'Innards Out', 'Libero', 'Magic Bounce', 'Magic Guard',
659
+ 'Magnet Pull', 'Mold Breaker', 'Moody', 'Neutralizing Gas', 'Power Construct', 'Queenly Majesty', 'Quick Draw',
660
+ 'Regenerator', 'Sand Rush', 'Sand Veil', 'Shadow Tag', 'Simple', 'Snow Cloak', 'Snow Warning ++ Slush Rush', 'Speed Boost',
661
+ 'Stakeout', 'Steelworker ++ Steely Spirit', 'Tinted Lens', 'Triage', 'Unaware', 'Unburden', 'Water Bubble',
662
+ 'Baton Pass',
653
663
  ],
654
- restricted: [
655
- 'Baneful Bunker', 'Block', 'Copycat', 'Corrosive Gas', 'Detect', 'Destiny Bond', 'Disable', 'Encore', 'Fairy Lock', 'Hypnosis', 'Ingrain',
656
- 'Instruct', 'Lovely Kiss', 'King\'s Shield', 'Mat Block', 'Mean Look', 'Memento', 'move:Metronome', 'Obstruct', 'Octolock', 'Nature Power',
657
- 'Parting Shot', 'Psycho Shift', 'Protect', 'Roar', 'Sing', 'Skill Swap', 'Sleep Powder', 'Sleep Talk', 'Spiky Shield', 'Spore', 'Substitute',
658
- 'Teleport', 'Whirlwind', 'Wish', 'Yawn',
664
+ getSharedPower(pokemon) {
665
+ const sharedPower = new Set<string>();
666
+ for (const ally of pokemon.side.pokemon) {
667
+ if (ally.previouslySwitchedIn > 0) {
668
+ if (pokemon.battle.dex.currentMod !== 'sharedpower' && ['trace', 'mirrorarmor'].includes(ally.baseAbility)) {
669
+ sharedPower.add('noability');
670
+ continue;
671
+ }
672
+ sharedPower.add(ally.baseAbility);
673
+ }
674
+ }
675
+ sharedPower.delete(pokemon.baseAbility);
676
+ return sharedPower;
677
+ },
678
+ onBeforeSwitchIn(pokemon) {
679
+ let format = this.format;
680
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
681
+ for (const ability of format.getSharedPower!(pokemon)) {
682
+ const effect = 'ability:' + ability;
683
+ pokemon.volatiles[effect] = {id: this.toID(effect), target: pokemon};
684
+ if (!pokemon.m.abils) pokemon.m.abils = [];
685
+ if (!pokemon.m.abils.includes(effect)) pokemon.m.abils.push(effect);
686
+ }
687
+ },
688
+ onSwitchInPriority: 2,
689
+ onSwitchIn(pokemon) {
690
+ let format = this.format;
691
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
692
+ for (const ability of format.getSharedPower!(pokemon)) {
693
+ if (ability === 'noability') {
694
+ this.hint(`Mirror Armor and Trace break in Shared Power formats that don't use Shared Power as a base, so they get removed from non-base users.`);
695
+ }
696
+ const effect = 'ability:' + ability;
697
+ delete pokemon.volatiles[effect];
698
+ pokemon.addVolatile(effect);
699
+ }
700
+ },
701
+ },
702
+ {
703
+ name: "[Gen 8] Cross Evolution",
704
+ desc: `Give a Pok&eacute;mon a Pok&eacute;mon name of the next evolution stage as a nickname to inherit stat changes, typing, abilities, and up to 2 moves from the next stage Pok&eacute;mon.`,
705
+ threads: [
706
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3657562/">Cross Evolution</a>`,
659
707
  ],
660
- onValidateTeam(team, format, teamHas) {
661
- const problems = [];
662
- for (const trademark in teamHas.trademarks) {
663
- if (teamHas.trademarks[trademark] > 1) {
664
- problems.push(`You are limited to 1 of each Trademark.`, `(You have ${teamHas.trademarks[trademark]} Pok\u00e9mon with ${trademark} as a Trademark.)`);
708
+
709
+ mod: 'gen8',
710
+ // searchShow: false,
711
+ ruleset: ['Standard', 'Overflow Stat Mod', 'Dynamax Clause'],
712
+ banlist: ['Corsola-Galar', 'Sneasel', 'Type: Null', 'Arena Trap', 'Ice Scales', 'Moody', 'King\'s Rock', 'Baton Pass'],
713
+ restricted: ['Chansey', 'Lunala', 'Shedinja', 'Solgaleo', 'Gorilla Tactics', 'Huge Power', 'Pure Power', 'Shadow Tag'],
714
+ onValidateTeam(team) {
715
+ const names = new Set<ID>();
716
+ for (const set of team) {
717
+ const name = set.name;
718
+ if (names.has(this.dex.toID(name))) {
719
+ return [
720
+ `Your Pok\u00e9mon must have different nicknames.`,
721
+ `(You have more than one Pok\u00e9mon named '${name}')`,
722
+ ];
665
723
  }
724
+ names.add(this.dex.toID(name));
725
+ }
726
+ if (!names.size) {
727
+ return [
728
+ `${this.format.name} works using nicknames; your team has 0 nicknamed Pok\u00e9mon.`,
729
+ `(If this was intentional, add a nickname to one Pok\u00e9mon that isn't the name of a Pok\u00e9mon species.)`,
730
+ ];
666
731
  }
667
- return problems;
732
+ },
733
+ checkCanLearn(move, species, lsetData, set) {
734
+ // @ts-ignore
735
+ if (!set.sp?.exists || !set.crossSpecies?.exists) {
736
+ return this.checkCanLearn(move, species, lsetData, set);
737
+ }
738
+ // @ts-ignore
739
+ const problem = this.checkCanLearn(move, set.sp);
740
+ if (!problem) return null;
741
+ // @ts-ignore
742
+ if (!set.crossMovesLeft) return problem;
743
+ // @ts-ignore
744
+ if (this.checkCanLearn(move, set.crossSpecies)) return problem;
745
+ // @ts-ignore
746
+ set.crossMovesLeft--;
747
+ return null;
668
748
  },
669
749
  validateSet(set, teamHas) {
670
- const dex = this.dex;
671
- const ability = dex.moves.get(set.ability);
672
- if (!ability.exists) { // Not even a real move
673
- return this.validateSet(set, teamHas);
750
+ const crossSpecies = this.dex.species.get(set.name);
751
+ let problems = this.dex.formats.get('Pokemon').onChangeSet?.call(this, set, this.format) || null;
752
+ if (Array.isArray(problems) && problems.length) return problems;
753
+ const crossNonstandard = (!this.ruleTable.has('standardnatdex') && crossSpecies.isNonstandard === 'Past') ||
754
+ crossSpecies.isNonstandard === 'Future';
755
+ const crossIsCap = !this.ruleTable.has('+pokemontag:cap') && crossSpecies.isNonstandard === 'CAP';
756
+ if (!crossSpecies.exists || crossNonstandard || crossIsCap) return this.validateSet(set, teamHas);
757
+ const species = this.dex.species.get(set.species);
758
+ const check = this.checkSpecies(set, species, species, {});
759
+ if (check) return [check];
760
+ const nonstandard = !this.ruleTable.has('standardnatdex') && species.isNonstandard === 'Past';
761
+ const isCap = !this.ruleTable.has('+pokemontag:cap') && species.isNonstandard === 'CAP';
762
+ if (!species.exists || nonstandard || isCap || species === crossSpecies) return this.validateSet(set, teamHas);
763
+ if (!species.nfe) return [`${species.name} cannot cross evolve because it doesn't evolve.`];
764
+ const crossIsUnreleased = (crossSpecies.tier === "Unreleased" && crossSpecies.isNonstandard === "Unobtainable");
765
+ if (crossSpecies.battleOnly || crossIsUnreleased || !crossSpecies.prevo) {
766
+ return [`${species.name} cannot cross evolve into ${crossSpecies.name} because it isn't an evolution.`];
674
767
  }
675
- // Absolute trademark bans
676
- if (ability.category !== 'Status') {
677
- return [`${ability.name} is not a status move, and cannot be used as a trademark.`];
768
+ if (this.ruleTable.isRestrictedSpecies(crossSpecies)) {
769
+ return [`${species.name} cannot cross evolve into ${crossSpecies.name} because it is banned.`];
678
770
  }
679
- if (ability.forceSwitch || ability.selfSwitch) {
771
+ const crossPrevoSpecies = this.dex.species.get(crossSpecies.prevo);
772
+ if (!crossPrevoSpecies.prevo !== !species.prevo) {
680
773
  return [
681
- `Force-switching and self-switching moves are banned from being used as trademarks.`,
682
- `(${ability.name} is a ${ability.forceSwitch ? 'force' : 'self'}-switching move.)`,
774
+ `${species.name} cannot cross evolve into ${crossSpecies.name} because they are not consecutive evolution stages.`,
683
775
  ];
684
776
  }
685
- const irrevokablyRestricted = [
686
- 'Assist', 'Copycat', 'Metronome', 'Mirror Move', 'Sleep Talk', // Could call another unsafe trademark
687
- 'Skill Swap', // Self-propagates indefinitely
688
- ];
689
- if (irrevokablyRestricted.includes(ability.name)) {
690
- return [`${ability.name} cannot safely function as a trademark.`];
777
+ const ability = this.dex.abilities.get(set.ability);
778
+ if (!this.ruleTable.isRestricted(`ability:${ability.id}`) || Object.values(species.abilities).includes(ability.name)) {
779
+ set.species = crossSpecies.name;
691
780
  }
692
- // Contingent trademark bans
693
- if (this.ruleTable.isRestricted(`move:${ability.id}`)) {
694
- return [`${ability.name} is restricted from being used as a trademark.`];
781
+
782
+ // @ts-ignore
783
+ set.sp = species;
784
+ // @ts-ignore
785
+ set.crossSpecies = crossSpecies;
786
+ // @ts-ignore
787
+ set.crossMovesLeft = 2;
788
+ problems = this.validateSet(set, teamHas);
789
+ set.name = crossSpecies.name;
790
+ set.species = species.name;
791
+ return problems;
792
+ },
793
+ onModifySpecies(species, target, source, effect) {
794
+ if (!target) return; // chat
795
+ if (effect && ['imposter', 'transform'].includes(effect.id)) return;
796
+ if (target.set.name === target.set.species) return;
797
+ const crossSpecies = this.dex.species.get(target.set.name);
798
+ if (!crossSpecies.exists) return;
799
+ if (species.battleOnly || !species.nfe) return;
800
+ const crossIsUnreleased = (crossSpecies.tier === "Unreleased" && crossSpecies.isNonstandard === "Unobtainable");
801
+ if (crossSpecies.battleOnly || crossIsUnreleased || !crossSpecies.prevo) return;
802
+ const crossPrevoSpecies = this.dex.species.get(crossSpecies.prevo);
803
+ if (!crossPrevoSpecies.prevo !== !species.prevo) return;
804
+
805
+ const mixedSpecies = this.dex.deepClone(species);
806
+ mixedSpecies.baseSpecies = mixedSpecies.name = `${species.name}-${crossSpecies.name}`;
807
+ mixedSpecies.weightkg =
808
+ Math.max(0.1, +(species.weightkg + crossSpecies.weightkg - crossPrevoSpecies.weightkg)).toFixed(1);
809
+ mixedSpecies.nfe = false;
810
+ mixedSpecies.evos = [];
811
+ mixedSpecies.eggGroups = crossSpecies.eggGroups;
812
+ mixedSpecies.abilities = crossSpecies.abilities;
813
+ mixedSpecies.bst = 0;
814
+ let i: StatID;
815
+ for (i in species.baseStats) {
816
+ const statChange = crossSpecies.baseStats[i] - crossPrevoSpecies.baseStats[i];
817
+ mixedSpecies.baseStats[i] = this.clampIntRange(species.baseStats[i] + statChange, 1, 255);
818
+ mixedSpecies.bst += mixedSpecies.baseStats[i];
695
819
  }
696
- if (set.moves.map(this.toID).includes(ability.id)) {
697
- return [`${set.name} may not use ${ability.name} as both a trademark and one of its moves simultaneously.`];
820
+ if (crossSpecies.types[0] !== crossPrevoSpecies.types[0]) mixedSpecies.types[0] = crossSpecies.types[0];
821
+ if (crossSpecies.types[1] !== crossPrevoSpecies.types[1]) {
822
+ mixedSpecies.types[1] = crossSpecies.types[1] || crossSpecies.types[0];
698
823
  }
699
- const customRules = this.format.customRules || [];
700
- if (!customRules.includes('!obtainableabilities')) customRules.push('!obtainableabilities');
701
-
702
- const TeamValidator: typeof import('../sim/team-validator').TeamValidator =
703
- require('../sim/team-validator').TeamValidator;
824
+ if (mixedSpecies.types[0] === mixedSpecies.types[1]) mixedSpecies.types = [mixedSpecies.types[0]];
704
825
 
705
- const validator = new TeamValidator(dex.formats.get(`${this.format.id}@@@${customRules.join(',')}`));
706
- const moves = set.moves;
707
- set.moves = [ability.id];
708
- set.ability = dex.species.get(set.species).abilities['0'];
709
- let problems = validator.validateSet(set, {}) || [];
710
- if (problems.length) return problems;
711
- set.moves = moves;
712
- set.ability = dex.species.get(set.species).abilities['0'];
713
- problems = problems.concat(validator.validateSet(set, teamHas) || []);
714
- set.ability = ability.id;
715
- if (!teamHas.trademarks) teamHas.trademarks = {};
716
- teamHas.trademarks[ability.name] = (teamHas.trademarks[ability.name] || 0) + 1;
717
- return problems.length ? problems : null;
826
+ return mixedSpecies;
718
827
  },
719
- pokemon: {
720
- getAbility() {
721
- const move = this.battle.dex.moves.get(this.battle.toID(this.ability));
722
- if (!move.exists) return Object.getPrototypeOf(this).getAbility.call(this);
723
- return {
724
- id: move.id,
725
- name: move.name,
726
- onStart(this: Battle, pokemon: Pokemon) {
727
- this.add('-activate', pokemon, 'ability: ' + move.name);
728
- this.actions.useMove(move, pokemon);
729
- },
730
- toString() {
731
- return "";
732
- },
733
- };
734
- },
735
- },
736
- },
737
- {
738
- name: "[Gen 8] Linked",
739
- desc: `The first two moves in a Pok&eacute;mon's moveset are used simultaneously.`,
740
- threads: [
741
- `&bullet; <a href="https://www.smogon.com/forums/threads/3660421/">Linked</a>`,
742
- ],
743
-
744
- mod: 'linked',
745
- ruleset: ['Standard', 'Dynamax Clause'],
746
- banlist: [
747
- 'Calyrex-Ice', 'Calyrex-Shadow', 'Cinderace', 'Cloyster', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina',
748
- 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kyogre', 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala', 'Magearna',
749
- 'Marshadow', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram', 'Solgaleo',
750
- 'Spectrier', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Zygarde-Base',
751
- 'Zygarde-Complete', 'Arena Trap', 'Chlorophyll', 'Moody', 'Power Construct', 'Sand Rush', 'Sand Veil', 'Shadow Tag', 'Slush Rush', 'Snow Cloak',
752
- 'Surge Surfer', 'Swift Swim', 'Unburden', 'Bright Powder', 'King\'s Rock', 'Lax Incense', 'Baton Pass',
753
- ],
754
- restricted: [
755
- 'Baneful Bunker', 'Bounce', 'Protect', 'Detect', 'Dig', 'Dive', 'Fly', 'King\'s Shield', 'Nature\'s Madness', 'Night Shade',
756
- 'Obstruct', 'Phantom Force', 'Seismic Toss', 'Shadow Force', 'Sky Drop', 'Spiky Shield', 'Super Fang', 'Trick Room',
757
- ],
758
- onValidateSet(set) {
759
- const problems = [];
760
- for (const [i, moveid] of set.moves.entries()) {
761
- const move = this.dex.moves.get(moveid);
762
- if ([0, 1].includes(i) && this.ruleTable.isRestricted(`move:${move.id}`)) {
763
- problems.push(`${set.name || set.species}'s move ${move.name} cannot be linked.`);
764
- }
828
+ onBegin() {
829
+ for (const pokemon of this.getAllPokemon()) {
830
+ pokemon.baseSpecies = pokemon.species;
765
831
  }
766
- return problems;
767
832
  },
768
833
  },
769
834
 
@@ -913,17 +978,17 @@ export const Formats: FormatList = [
913
978
  mod: 'gen8',
914
979
  ruleset: ['Standard', 'STABmons Move Legality', 'Dynamax Clause', 'Sleep Moves Clause', '!Sleep Clause Mod'],
915
980
  banlist: [
916
- 'Aegislash', 'Blacephalon', 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragapult', 'Dragonite',
917
- 'Eternatus', 'Genesect', 'Garchomp', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem-Black',
918
- 'Kyurem-White', 'Landorus', 'Landorus-Therian', 'Lugia', 'Lunala', 'Marshadow', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings',
919
- 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Porygon-Z', 'Rayquaza', 'Reshiram', 'Silvally', 'Solgaleo', 'Spectrier', 'Tapu Bulu',
920
- 'Tapu Koko', 'Thundurus-Base', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned',
921
- 'Zapdos-Galar', 'Zekrom', 'Zygarde-Base', 'Arena Trap', 'Magnet Pull', 'Moody', 'Power Construct', 'Shadow Tag', 'King\'s Rock',
922
- 'Baton Pass',
981
+ 'Aegislash', 'Blacephalon', 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragapult', 'Dragonite', 'Eternatus',
982
+ 'Genesect', 'Garchomp', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus',
983
+ 'Landorus-Therian', 'Lugia', 'Lunala', 'Magearna', 'Marshadow', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia',
984
+ 'Pheromosa', 'Porygon-Z', 'Rayquaza', 'Reshiram', 'Silvally', 'Solgaleo', 'Spectrier', 'Tapu Bulu', 'Tapu Koko', 'Tapu Lele', 'Thundurus-Base',
985
+ 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zapdos-Galar', 'Zekrom', 'Zygarde-Base',
986
+ 'Arena Trap', 'Magnet Pull', 'Moody', 'Power Construct', 'Shadow Tag', 'King\'s Rock', 'Baton Pass',
923
987
  ],
924
988
  restricted: [
925
989
  'Acupressure', 'Astral Barrage', 'Belly Drum', 'Bolt Beak', 'Clangorous Soul', 'Double Iron Bash', 'Electrify', 'Extreme Speed', 'Fishious Rend',
926
- 'Geomancy', 'Glacial Lance', 'Oblivion Wing', 'Precipice Blades', 'Shell Smash', 'Shift Gear', 'Thousand Arrows', 'V-create', 'Wicked Blow',
990
+ 'Geomancy', 'Glacial Lance', 'Oblivion Wing', 'Precipice Blades', 'Shell Smash', 'Shift Gear', 'Thousand Arrows', 'Thunderous Kick', 'V-create',
991
+ 'Wicked Blow',
927
992
  ],
928
993
  },
929
994
  {
@@ -1082,7 +1147,6 @@ export const Formats: FormatList = [
1082
1147
  ],
1083
1148
 
1084
1149
  mod: 'gen8bdsp',
1085
- searchShow: false,
1086
1150
  ruleset: ['[Gen 8 BDSP] RU'],
1087
1151
  banlist: ['RU', 'NUBL'],
1088
1152
  },
@@ -1173,7 +1237,7 @@ export const Formats: FormatList = [
1173
1237
 
1174
1238
  mod: 'gen8',
1175
1239
  searchShow: false,
1176
- ruleset: ['Obtainable', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Team Preview', 'HP Percentage Mod', 'Cancel Mod', 'Dynamax Clause', 'Sleep Clause Mod', 'Endless Battle Clause'],
1240
+ ruleset: ['Standard', 'Dynamax Clause', 'Camomons Mod'],
1177
1241
  banlist: [
1178
1242
  'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragonite', 'Eternatus', 'Genesect', 'Giratina', 'Giratina-Origin',
1179
1243
  'Groudon', 'Ho-Oh', 'Hydreigon', 'Kartana', 'Kyogre', 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Latias', 'Latios', 'Lugia',
@@ -1181,19 +1245,6 @@ export const Formats: FormatList = [
1181
1245
  'Reuniclus', 'Shedinja', 'Slowking-Galar', 'Solgaleo', 'Spectrier', 'Tornadus-Therian', 'Volcarona', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned',
1182
1246
  'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Zeraora', 'Zygarde-Base', 'Arena Trap', 'Moody', 'Power Construct', 'Shadow Tag', 'Baton Pass',
1183
1247
  ],
1184
- onModifySpeciesPriority: 2,
1185
- onModifySpecies(species, target, source, effect) {
1186
- if (!target) return; // Chat command
1187
- if (effect && ['imposter', 'transform'].includes(effect.id)) return;
1188
- const types = [...new Set(target.baseMoveSlots.slice(0, 2).map(move => this.dex.moves.get(move.id).type))];
1189
- return {...species, types: types};
1190
- },
1191
- onSwitchIn(pokemon) {
1192
- this.add('-start', pokemon, 'typechange', (pokemon.illusion || pokemon).getTypes(true).join('/'), '[silent]');
1193
- },
1194
- onAfterMega(pokemon) {
1195
- this.add('-start', pokemon, 'typechange', (pokemon.illusion || pokemon).getTypes(true).join('/'), '[silent]');
1196
- },
1197
1248
  },
1198
1249
  {
1199
1250
  name: "[Gen 8] Inheritance",
@@ -1377,6 +1428,39 @@ export const Formats: FormatList = [
1377
1428
  this.add('-start', pokemon, donorTemplate.name, '[silent]');
1378
1429
  },
1379
1430
  },
1431
+ {
1432
+ name: "[Gen 8] Linked",
1433
+ desc: `The first two moves in a Pok&eacute;mon's moveset are used simultaneously.`,
1434
+ threads: [
1435
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3660421/">Linked</a>`,
1436
+ ],
1437
+
1438
+ mod: 'linked',
1439
+ searchShow: false,
1440
+ ruleset: ['Standard', 'Dynamax Clause'],
1441
+ banlist: [
1442
+ 'Calyrex-Ice', 'Calyrex-Shadow', 'Cinderace', 'Cloyster', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina',
1443
+ 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala',
1444
+ 'Magearna', 'Marshadow', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram',
1445
+ 'Solgaleo', 'Spectrier', 'Urshifu-Base', 'Volcarona', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned',
1446
+ 'Zekrom', 'Zygarde-Base', 'Zygarde-Complete', 'Arena Trap', 'Chlorophyll', 'Moody', 'Power Construct', 'Sand Rush', 'Sand Veil', 'Shadow Tag',
1447
+ 'Slush Rush', 'Snow Cloak', 'Speed Boost', 'Surge Surfer', 'Swift Swim', 'Unburden', 'Bright Powder', 'King\'s Rock', 'Lax Incense', 'Baton Pass',
1448
+ ],
1449
+ restricted: [
1450
+ 'Baneful Bunker', 'Bounce', 'Protect', 'Detect', 'Dig', 'Dive', 'Fly', 'King\'s Shield', 'Nature\'s Madness', 'Night Shade',
1451
+ 'Obstruct', 'Phantom Force', 'Seismic Toss', 'Shadow Force', 'Sky Drop', 'Spiky Shield', 'Super Fang', 'Trick Room',
1452
+ ],
1453
+ onValidateSet(set) {
1454
+ const problems = [];
1455
+ for (const [i, moveid] of set.moves.entries()) {
1456
+ const move = this.dex.moves.get(moveid);
1457
+ if ([0, 1].includes(i) && this.ruleTable.isRestricted(`move:${move.id}`)) {
1458
+ problems.push(`${set.name || set.species}'s move ${move.name} cannot be linked.`);
1459
+ }
1460
+ }
1461
+ return problems;
1462
+ },
1463
+ },
1380
1464
  {
1381
1465
  name: "[Gen 8] Multibility",
1382
1466
  desc: `Run a second ability at the cost of giving up a Pok&eacute;mon's item slot.`,
@@ -1643,6 +1727,21 @@ export const Formats: FormatList = [
1643
1727
  // Shadow Tag/Arena Trap
1644
1728
  'Diglett-Base', 'Dugtrio-Base', 'Gothita', 'Gothitelle', 'Gothorita', 'Trapinch', 'Wobbuffet', 'Wynaut',
1645
1729
  ],
1730
+ onValidateSet(set) {
1731
+ const species = this.dex.species.get(set.species);
1732
+ const unSeenAbilities = Object.keys(species.abilities)
1733
+ .filter(key => key !== 'S' && (key !== 'H' || !species.unreleasedHidden))
1734
+ .map(key => species.abilities[key as "0" | "1" | "H" | "S"])
1735
+ .filter(ability => ability !== set.ability);
1736
+ if (unSeenAbilities.length && this.toID(set.ability) !== this.toID(species.abilities['S'])) {
1737
+ for (const abilityName of unSeenAbilities) {
1738
+ const banReason = this.ruleTable.check('ability:' + this.toID(abilityName));
1739
+ if (banReason) {
1740
+ return [`${set.name}'s ability ${abilityName} is ${banReason}.`];
1741
+ }
1742
+ }
1743
+ }
1744
+ },
1646
1745
  onBegin() {
1647
1746
  for (const pokemon of this.getAllPokemon()) {
1648
1747
  if (pokemon.ability === this.toID(pokemon.species.abilities['S'])) {
@@ -1670,64 +1769,113 @@ export const Formats: FormatList = [
1670
1769
  },
1671
1770
  },
1672
1771
  {
1673
- name: "[Gen 8] Shared Power",
1674
- desc: `Once a Pok&eacute;mon switches in, its ability is shared with the rest of the team.`,
1772
+ name: "[Gen 8] Trademarked",
1773
+ desc: `Sacrifice your Pok&eacute;mon's ability for a status move that activates on switch-in.`,
1675
1774
  threads: [
1676
- `&bullet; <a href="https://www.smogon.com/forums/threads/3660877/">Shared Power</a>`,
1775
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3656980/">Trademarked</a>`,
1677
1776
  ],
1678
1777
 
1679
- mod: 'sharedpower',
1778
+ mod: 'gen8',
1779
+ // While bugs are being fixed
1680
1780
  searchShow: false,
1781
+ challengeShow: false,
1782
+ tournamentShow: false,
1681
1783
  ruleset: ['Standard', 'Dynamax Clause'],
1682
1784
  banlist: [
1683
- 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina',
1684
- 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna',
1685
- 'Marshadow', 'Melmetal', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia',
1686
- 'Pheromosa', 'Rayquaza', 'Reshiram', 'Shedinja', 'Solgaleo', 'Urshifu-Base', 'Urshifu-Rapid-Strike',
1687
- 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom',
1688
- 'Arena Trap', 'Contrary', 'Drizzle ++ Swift Swim', 'Drought ++ Chlorophyll', 'Electric Surge ++ Surge Surfer',
1689
- 'Fur Coat', 'Guts', 'Harvest', 'Huge Power', 'Imposter', 'Innards Out', 'Libero', 'Magic Bounce', 'Magic Guard',
1690
- 'Magnet Pull', 'Mold Breaker', 'Moody', 'Neutralizing Gas', 'Power Construct', 'Queenly Majesty', 'Quick Draw',
1691
- 'Regenerator', 'Sand Rush', 'Sand Veil', 'Shadow Tag', 'Simple', 'Snow Cloak', 'Snow Warning ++ Slush Rush', 'Speed Boost',
1692
- 'Stakeout', 'Steelworker ++ Steely Spirit', 'Tinted Lens', 'Triage', 'Unaware', 'Unburden', 'Water Bubble',
1693
- 'Baton Pass',
1785
+ 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragapult', 'Eternatus', 'Kyurem-Black', 'Kyurem-White', 'Giratina',
1786
+ 'Giratina-Origin', 'Genesect', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Lugia', 'Lunala', 'Magearna', 'Marowak-Alola', 'Marshadow', 'Melmetal',
1787
+ 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram', 'Solgaleo', 'Spectrier',
1788
+ 'Urshifu-Base', 'Victini', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Zygarde-Base',
1789
+ 'Arena Trap', 'Moody', 'Neutralizing Gas', 'Power Construct', 'Shadow Tag', 'Baton Pass',
1694
1790
  ],
1695
- getSharedPower(pokemon) {
1696
- const sharedPower = new Set<string>();
1697
- for (const ally of pokemon.side.pokemon) {
1698
- if (ally.previouslySwitchedIn > 0) {
1699
- if (pokemon.battle.dex.currentMod !== 'sharedpower' && ['trace', 'mirrorarmor'].includes(ally.baseAbility)) {
1700
- sharedPower.add('noability');
1701
- continue;
1702
- }
1703
- sharedPower.add(ally.baseAbility);
1791
+ restricted: [
1792
+ 'Baneful Bunker', 'Block', 'Copycat', 'Corrosive Gas', 'Detect', 'Destiny Bond', 'Disable', 'Encore', 'Fairy Lock', 'Hypnosis', 'Ingrain',
1793
+ 'Instruct', 'Lovely Kiss', 'King\'s Shield', 'Mat Block', 'Mean Look', 'Memento', 'move:Metronome', 'Obstruct', 'Octolock', 'Nature Power',
1794
+ 'Parting Shot', 'Psycho Shift', 'Protect', 'Roar', 'Sing', 'Skill Swap', 'Sleep Powder', 'Sleep Talk', 'Spiky Shield', 'Spore', 'Substitute',
1795
+ 'Switcheroo', 'Teleport', 'Trick', 'Whirlwind', 'Wish', 'Yawn',
1796
+ ],
1797
+ onValidateTeam(team, format, teamHas) {
1798
+ const problems = [];
1799
+ for (const trademark in teamHas.trademarks) {
1800
+ if (teamHas.trademarks[trademark] > 1) {
1801
+ problems.push(`You are limited to 1 of each Trademark.`, `(You have ${teamHas.trademarks[trademark]} Pok\u00e9mon with ${trademark} as a Trademark.)`);
1704
1802
  }
1705
1803
  }
1706
- sharedPower.delete(pokemon.baseAbility);
1707
- return sharedPower;
1804
+ return problems;
1708
1805
  },
1709
- onBeforeSwitchIn(pokemon) {
1710
- let format = this.format;
1711
- if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
1712
- for (const ability of format.getSharedPower!(pokemon)) {
1713
- const effect = 'ability:' + ability;
1714
- pokemon.volatiles[effect] = {id: this.toID(effect), target: pokemon};
1715
- if (!pokemon.m.abils) pokemon.m.abils = [];
1716
- if (!pokemon.m.abils.includes(effect)) pokemon.m.abils.push(effect);
1806
+ validateSet(set, teamHas) {
1807
+ const dex = this.dex;
1808
+ const ability = dex.moves.get(set.ability);
1809
+ if (!ability.exists) { // Not even a real move
1810
+ return this.validateSet(set, teamHas);
1717
1811
  }
1718
- },
1719
- onSwitchInPriority: 2,
1720
- onSwitchIn(pokemon) {
1721
- let format = this.format;
1722
- if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
1723
- for (const ability of format.getSharedPower!(pokemon)) {
1724
- if (ability === 'noability') {
1725
- this.hint(`Mirror Armor and Trace break in Shared Power formats that don't use Shared Power as a base, so they get removed from non-base users.`);
1812
+ // Absolute trademark bans
1813
+ if (ability.category !== 'Status') {
1814
+ return [`${ability.name} is not a status move, and cannot be used as a trademark.`];
1815
+ }
1816
+ if (ability.forceSwitch || ability.selfSwitch) {
1817
+ return [
1818
+ `Force-switching and self-switching moves are banned from being used as trademarks.`,
1819
+ `(${ability.name} is a ${ability.forceSwitch ? 'force' : 'self'}-switching move.)`,
1820
+ ];
1821
+ }
1822
+ const irrevokablyRestricted = [
1823
+ 'Assist', 'Copycat', 'Metronome', 'Mirror Move', 'Sleep Talk', // Could call another unsafe trademark
1824
+ 'Recycle', 'Trace', // Causes endless turns
1825
+ 'Skill Swap', // Self-propagates indefinitely
1826
+ ];
1827
+ for (const m of set.moves) {
1828
+ const move = dex.moves.get(m);
1829
+ if (irrevokablyRestricted.includes(move.name)) {
1830
+ return [`${move.name} is banned from Trademark, irrespective of custom rules, because it can cause endless turns.`];
1726
1831
  }
1727
- const effect = 'ability:' + ability;
1728
- delete pokemon.volatiles[effect];
1729
- pokemon.addVolatile(effect);
1730
1832
  }
1833
+ if (irrevokablyRestricted.includes(ability.name)) {
1834
+ return [`${ability.name} cannot safely function as a trademark.`];
1835
+ }
1836
+ // Contingent trademark bans
1837
+ if (this.ruleTable.isRestricted(`move:${ability.id}`)) {
1838
+ return [`${ability.name} is restricted from being used as a trademark.`];
1839
+ }
1840
+ if (set.moves.map(this.toID).includes(ability.id)) {
1841
+ return [`${set.name} may not use ${ability.name} as both a trademark and one of its moves simultaneously.`];
1842
+ }
1843
+ const customRules = this.format.customRules || [];
1844
+ if (!customRules.includes('!obtainableabilities')) customRules.push('!obtainableabilities');
1845
+
1846
+ const TeamValidator: typeof import('../sim/team-validator').TeamValidator =
1847
+ require('../sim/team-validator').TeamValidator;
1848
+
1849
+ const validator = new TeamValidator(dex.formats.get(`${this.format.id}@@@${customRules.join(',')}`));
1850
+ const moves = set.moves;
1851
+ set.moves = [ability.id];
1852
+ set.ability = dex.species.get(set.species).abilities['0'];
1853
+ let problems = validator.validateSet(set, {}) || [];
1854
+ if (problems.length) return problems;
1855
+ set.moves = moves;
1856
+ set.ability = dex.species.get(set.species).abilities['0'];
1857
+ problems = problems.concat(validator.validateSet(set, teamHas) || []);
1858
+ set.ability = ability.id;
1859
+ if (!teamHas.trademarks) teamHas.trademarks = {};
1860
+ teamHas.trademarks[ability.name] = (teamHas.trademarks[ability.name] || 0) + 1;
1861
+ return problems.length ? problems : null;
1862
+ },
1863
+ pokemon: {
1864
+ getAbility() {
1865
+ const move = this.battle.dex.moves.get(this.battle.toID(this.ability));
1866
+ if (!move.exists) return Object.getPrototypeOf(this).getAbility.call(this);
1867
+ return {
1868
+ id: move.id,
1869
+ name: move.name,
1870
+ onStart(this: Battle, pokemon: Pokemon) {
1871
+ this.add('-activate', pokemon, 'ability: ' + move.name);
1872
+ this.actions.useMove(move, pokemon);
1873
+ },
1874
+ toString() {
1875
+ return "";
1876
+ },
1877
+ };
1878
+ },
1731
1879
  },
1732
1880
  },
1733
1881
  {
@@ -1888,6 +2036,38 @@ export const Formats: FormatList = [
1888
2036
  searchShow: false,
1889
2037
  ruleset: ['[Gen 8] Random Battle', 'Dynamax Clause'],
1890
2038
  },
2039
+ {
2040
+ name: "[Gen 8] Random Battle Mayhem",
2041
+ desc: `[Gen 8] Random Battle (No Dmax) with Team Preview and elements of Camomons, Inverse, Scalemons, and Shared Power.`,
2042
+
2043
+ mod: 'sharedpower',
2044
+ team: 'random',
2045
+ searchShow: false,
2046
+ ruleset: ['[Gen 8] Random Battle', 'Team Preview', 'Dynamax Clause', 'Camomons Mod', 'Inverse Mod', 'Scalemons Mod'],
2047
+ onBeforeSwitchIn(pokemon) {
2048
+ let format = this.format;
2049
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
2050
+ for (const ability of format.getSharedPower!(pokemon)) {
2051
+ const effect = 'ability:' + ability;
2052
+ pokemon.volatiles[effect] = {id: this.toID(effect), target: pokemon};
2053
+ if (!pokemon.m.abils) pokemon.m.abils = [];
2054
+ if (!pokemon.m.abils.includes(effect)) pokemon.m.abils.push(effect);
2055
+ }
2056
+ },
2057
+ onSwitchInPriority: 2,
2058
+ onSwitchIn(pokemon) {
2059
+ let format = this.format;
2060
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
2061
+ for (const ability of format.getSharedPower!(pokemon)) {
2062
+ if (ability === 'noability') {
2063
+ this.hint(`Mirror Armor and Trace break in Shared Power formats that don't use Shared Power as a base, so they get removed from non-base users.`);
2064
+ }
2065
+ const effect = 'ability:' + ability;
2066
+ delete pokemon.volatiles[effect];
2067
+ pokemon.addVolatile(effect);
2068
+ }
2069
+ },
2070
+ },
1891
2071
  {
1892
2072
  name: "[Gen 8] BSS Factory",
1893
2073
  desc: `Randomized 3v3 Singles featuring Pok&eacute;mon and movesets popular in Battle Stadium Singles.`,
@@ -2165,39 +2345,45 @@ export const Formats: FormatList = [
2165
2345
  column: 3,
2166
2346
  },
2167
2347
  {
2168
- name: "[Gen 3] UU",
2348
+ name: "[Gen 4] UU",
2169
2349
  threads: [
2170
- `&bullet; <a href="https://www.smogon.com/forums/threads/3585923/">ADV UU Metagame Discussion</a>`,
2171
- `&bullet; <a href="https://www.smogon.com/forums/threads/3548578/">ADV UU Viability Rankings</a>`,
2350
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3532624/">DPP UU Metagame Discussion</a>`,
2351
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3503638/">DPP UU Viability Rankings</a>`,
2172
2352
  ],
2173
2353
 
2174
- mod: 'gen3',
2354
+ mod: 'gen4',
2175
2355
  // searchShow: false,
2176
- ruleset: ['Standard', 'NFE Clause'],
2177
- banlist: ['Uber', 'OU', 'UUBL', 'Smeargle + Ingrain', 'Baton Pass'],
2178
- unbanlist: ['Scyther', 'Sand Veil'],
2356
+ ruleset: ['[Gen 4] OU'],
2357
+ banlist: ['OU', 'UUBL'],
2358
+ unbanlist: ['Arena Trap', 'Swagger'],
2179
2359
  },
2180
2360
  {
2181
- name: "[Gen 1] NU",
2361
+ name: "[Gen 7] LC",
2182
2362
  threads: [
2183
- `&bullet; <a href="https://www.smogon.com/forums/threads/3668913/">RBY NU Viability Rankings</a>`,
2363
+ `&bullet; <a href="https://www.smogon.com/dex/sm/formats/lc/">USM LC Banlist</a>`,
2364
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3639319/">USM LC Sample Teams</a>`,
2365
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3621440/">USM LC Viability Rankings</a>`,
2184
2366
  ],
2185
2367
 
2186
- mod: 'gen1',
2368
+ mod: 'gen7',
2187
2369
  // searchShow: false,
2188
- ruleset: ['[Gen 1] UU'],
2189
- banlist: ['UU', 'NUBL'],
2370
+ ruleset: ['Little Cup', 'Standard', 'Swagger Clause'],
2371
+ banlist: [
2372
+ 'Aipom', 'Cutiefly', 'Drifloon', 'Gligar', 'Gothita', 'Meditite', 'Misdreavus', 'Murkrow', 'Porygon',
2373
+ 'Scyther', 'Sneasel', 'Swirlix', 'Tangela', 'Trapinch', 'Vulpix-Base', 'Wingull', 'Yanma',
2374
+ 'Eevium Z', 'Baton Pass', 'Dragon Rage', 'Sonic Boom', 'Sticky Web',
2375
+ ],
2190
2376
  },
2191
2377
  {
2192
- name: "[Gen 4] Ubers",
2378
+ name: "[Gen 5] PU",
2193
2379
  threads: [
2194
- `&bullet; <a href="https://www.smogon.com/forums/posts/8286279/">DPP Ubers</a>`,
2380
+ `&bullet; <a href="https://www.smogon.com/forums/posts/7326932/">BW2 PU</a>`,
2195
2381
  ],
2196
2382
 
2197
- mod: 'gen4',
2383
+ mod: 'gen5',
2198
2384
  // searchShow: false,
2199
- ruleset: ['Standard'],
2200
- banlist: ['AG'],
2385
+ ruleset: ['[Gen 5] NU', 'Sleep Moves Clause'],
2386
+ banlist: ['NU', 'PUBL'],
2201
2387
  },
2202
2388
 
2203
2389
  // Past Gens OU
@@ -2490,23 +2676,6 @@ export const Formats: FormatList = [
2490
2676
  ruleset: ['[Gen 7] NU'],
2491
2677
  banlist: ['NU', 'PUBL'],
2492
2678
  },
2493
- {
2494
- name: "[Gen 7] LC",
2495
- threads: [
2496
- `&bullet; <a href="https://www.smogon.com/dex/sm/formats/lc/">USM LC Banlist</a>`,
2497
- `&bullet; <a href="https://www.smogon.com/forums/threads/3639319/">USM LC Sample Teams</a>`,
2498
- `&bullet; <a href="https://www.smogon.com/forums/threads/3621440/">USM LC Viability Rankings</a>`,
2499
- ],
2500
-
2501
- mod: 'gen7',
2502
- searchShow: false,
2503
- ruleset: ['Little Cup', 'Standard', 'Swagger Clause'],
2504
- banlist: [
2505
- 'Aipom', 'Cutiefly', 'Drifloon', 'Gligar', 'Gothita', 'Meditite', 'Misdreavus', 'Murkrow', 'Porygon',
2506
- 'Scyther', 'Sneasel', 'Swirlix', 'Tangela', 'Trapinch', 'Vulpix-Base', 'Wingull', 'Yanma',
2507
- 'Eevium Z', 'Baton Pass', 'Dragon Rage', 'Sonic Boom', 'Sticky Web',
2508
- ],
2509
- },
2510
2679
  {
2511
2680
  name: "[Gen 7] Monotype",
2512
2681
  desc: `All the Pok&eacute;mon on a team must share a type.`,
@@ -2850,14 +3019,15 @@ export const Formats: FormatList = [
2850
3019
  mod: 'gen6',
2851
3020
  searchShow: false,
2852
3021
  ruleset: [
2853
- 'Max Team Size = 3', 'Picked Team Size = 1',
2854
- 'Obtainable', 'Nickname Clause', 'Moody Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Accuracy Moves Clause', 'Swagger Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod', 'Team Preview',
3022
+ 'Max Team Size = 3', 'Picked Team Size = 1', 'Obtainable', 'Nickname Clause', 'Moody Clause', 'OHKO Clause',
3023
+ 'Evasion Moves Clause', 'Accuracy Moves Clause', 'Swagger Clause', 'Endless Battle Clause', 'HP Percentage Mod',
3024
+ 'Cancel Mod', 'Team Preview',
2855
3025
  ],
2856
3026
  banlist: [
2857
3027
  'Arceus', 'Blaziken', 'Darkrai', 'Deoxys-Base', 'Deoxys-Attack', 'Deoxys-Defense', 'Dialga', 'Giratina',
2858
3028
  'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kangaskhan-Mega', 'Kyogre', 'Kyurem-White', 'Lugia', 'Mewtwo',
2859
3029
  'Palkia', 'Rayquaza', 'Reshiram', 'Salamence-Mega', 'Shaymin-Sky', 'Xerneas', 'Yveltal', 'Zekrom',
2860
- 'Focus Sash', 'Soul Dew', 'Perish Song',
3030
+ 'Focus Sash', 'Soul Dew', 'Grass Whistle', 'Hypnosis', 'Perish Song', 'Sing', 'Yawn',
2861
3031
  ],
2862
3032
  },
2863
3033
  {
@@ -3070,17 +3240,6 @@ export const Formats: FormatList = [
3070
3240
  ruleset: ['[Gen 5] RU', '!Sleep Moves Clause', 'Sleep Clause Mod'],
3071
3241
  banlist: ['RU', 'NUBL', 'Assist', 'Copycat'],
3072
3242
  },
3073
- {
3074
- name: "[Gen 5] PU",
3075
- threads: [
3076
- `&bullet; <a href="https://www.smogon.com/forums/posts/7326932/">BW2 PU</a>`,
3077
- ],
3078
-
3079
- mod: 'gen5',
3080
- searchShow: false,
3081
- ruleset: ['[Gen 5] NU', 'Sleep Moves Clause'],
3082
- banlist: ['NU', 'PUBL'],
3083
- },
3084
3243
  {
3085
3244
  name: "[Gen 5] LC",
3086
3245
  threads: [
@@ -3120,7 +3279,7 @@ export const Formats: FormatList = [
3120
3279
  'Picked Team Size = 1', 'Max Team Size = 3',
3121
3280
  'Standard', 'Baton Pass Clause', 'Swagger Clause', 'Accuracy Moves Clause',
3122
3281
  ],
3123
- banlist: ['Uber', 'Cottonee', 'Dragonite', 'Kyurem-Black', 'Mew', 'Togekiss', 'Whimsicott', 'Victini', 'Bright Powder', 'Focus Band', 'Focus Sash', 'Lax Incense', 'Quick Claw', 'Soul Dew', 'Perish Song'],
3282
+ banlist: ['Uber', 'Cottonee', 'Dragonite', 'Jirachi', 'Kyurem-Black', 'Mew', 'Togekiss', 'Whimsicott', 'Victini', 'Bright Powder', 'Focus Band', 'Focus Sash', 'Lax Incense', 'Quick Claw', 'Soul Dew', 'Perish Song'],
3124
3283
  unbanlist: ['Genesect', 'Landorus', 'Manaphy', 'Thundurus', 'Tornadus-Therian'],
3125
3284
  },
3126
3285
  {
@@ -3221,17 +3380,15 @@ export const Formats: FormatList = [
3221
3380
  column: 5,
3222
3381
  },
3223
3382
  {
3224
- name: "[Gen 4] UU",
3383
+ name: "[Gen 4] Ubers",
3225
3384
  threads: [
3226
- `&bullet; <a href="https://www.smogon.com/forums/threads/3532624/">DPP UU Metagame Discussion</a>`,
3227
- `&bullet; <a href="https://www.smogon.com/forums/threads/3503638/">DPP UU Viability Rankings</a>`,
3385
+ `&bullet; <a href="https://www.smogon.com/forums/posts/8286279/">DPP Ubers</a>`,
3228
3386
  ],
3229
3387
 
3230
3388
  mod: 'gen4',
3231
3389
  searchShow: false,
3232
- ruleset: ['[Gen 4] OU'],
3233
- banlist: ['OU', 'UUBL'],
3234
- unbanlist: ['Arena Trap', 'Swagger'],
3390
+ ruleset: ['Standard'],
3391
+ banlist: ['AG'],
3235
3392
  },
3236
3393
  {
3237
3394
  name: "[Gen 4] NU",
@@ -3379,6 +3536,19 @@ export const Formats: FormatList = [
3379
3536
  ruleset: ['Standard', 'Deoxys Camouflage Clause', 'One Baton Pass Clause'],
3380
3537
  banlist: ['Wobbuffet + Leftovers'],
3381
3538
  },
3539
+ {
3540
+ name: "[Gen 3] UU",
3541
+ threads: [
3542
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3585923/">ADV UU Metagame Discussion</a>`,
3543
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3548578/">ADV UU Viability Rankings</a>`,
3544
+ ],
3545
+
3546
+ mod: 'gen3',
3547
+ searchShow: false,
3548
+ ruleset: ['Standard', 'NFE Clause'],
3549
+ banlist: ['Uber', 'OU', 'UUBL', 'Smeargle + Ingrain', 'Baton Pass'],
3550
+ unbanlist: ['Scyther', 'Sand Veil'],
3551
+ },
3382
3552
  {
3383
3553
  name: "[Gen 3] NU",
3384
3554
  threads: [
@@ -3477,7 +3647,7 @@ export const Formats: FormatList = [
3477
3647
  '[Gen 2] OU', 'Accuracy Moves Clause', 'Sleep Moves Clause', 'Team Preview',
3478
3648
  ],
3479
3649
  banlist: [
3480
- 'Clefable', 'Snorlax', 'Zapdos', 'Berserk Gene', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Quick Claw',
3650
+ 'Alakazam', 'Clefable', 'Snorlax', 'Zapdos', 'Berserk Gene', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Quick Claw',
3481
3651
  'Attract', 'Destiny Bond', 'Explosion', 'Perish Song', 'Present', 'Self-Destruct', 'Swagger',
3482
3652
  ],
3483
3653
  },
@@ -3538,6 +3708,17 @@ export const Formats: FormatList = [
3538
3708
  ruleset: ['[Gen 1] OU'],
3539
3709
  banlist: ['OU', 'UUBL'],
3540
3710
  },
3711
+ {
3712
+ name: "[Gen 1] NU",
3713
+ threads: [
3714
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3668913/">RBY NU Viability Rankings</a>`,
3715
+ ],
3716
+
3717
+ mod: 'gen1',
3718
+ searchShow: false,
3719
+ ruleset: ['[Gen 1] UU'],
3720
+ banlist: ['UU', 'NUBL'],
3721
+ },
3541
3722
  {
3542
3723
  name: "[Gen 1] Japanese OU",
3543
3724
  desc: `Generation 1 with Japanese battle mechanics.`,