@pkmn/sim 0.5.22 → 0.5.23

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 (42) hide show
  1. package/build/config/formats.js +296 -325
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/aliases.js +4 -4
  4. package/build/data/aliases.js.map +1 -1
  5. package/build/data/conditions.js +1 -1
  6. package/build/data/conditions.js.map +1 -1
  7. package/build/data/formats-data.js +7 -8
  8. package/build/data/formats-data.js.map +1 -1
  9. package/build/data/mods/gen1/formats-data.js +1 -1
  10. package/build/data/mods/gen1/formats-data.js.map +1 -1
  11. package/build/data/mods/gen1/moves.js +5 -0
  12. package/build/data/mods/gen1/moves.js.map +1 -1
  13. package/build/data/mods/gen1/scripts.js +4 -2
  14. package/build/data/mods/gen1/scripts.js.map +1 -1
  15. package/build/data/mods/gen3/formats-data.js +3 -3
  16. package/build/data/mods/gen3/formats-data.js.map +1 -1
  17. package/build/data/mods/gen6/formats-data.js +64 -64
  18. package/build/data/mods/gen6/formats-data.js.map +1 -1
  19. package/build/data/rulesets.js +108 -1
  20. package/build/data/rulesets.js.map +1 -1
  21. package/build/sim/battle-stream.js +3 -0
  22. package/build/sim/battle-stream.js.map +1 -1
  23. package/build/sim/exported-global-types.d.ts +2 -0
  24. package/build/sim/global-types.d.ts +2 -0
  25. package/build/sim/pokemon.d.ts +1 -0
  26. package/build/sim/pokemon.js +1 -0
  27. package/build/sim/pokemon.js.map +1 -1
  28. package/config/formats.ts +294 -318
  29. package/data/aliases.ts +4 -4
  30. package/data/conditions.ts +1 -1
  31. package/data/formats-data.ts +7 -8
  32. package/data/mods/gen1/formats-data.ts +1 -1
  33. package/data/mods/gen1/moves.ts +5 -0
  34. package/data/mods/gen1/scripts.ts +3 -2
  35. package/data/mods/gen3/formats-data.ts +3 -3
  36. package/data/mods/gen6/formats-data.ts +64 -64
  37. package/data/rulesets.ts +92 -1
  38. package/package.json +2 -2
  39. package/sim/battle-stream.ts +3 -0
  40. package/sim/exported-global-types.ts +2 -0
  41. package/sim/global-types.ts +2 -0
  42. package/sim/pokemon.ts +2 -0
package/config/formats.ts CHANGED
@@ -235,8 +235,9 @@ export const Formats: FormatList = [
235
235
  mod: 'gen8',
236
236
  ruleset: ['[Gen 8] PU'],
237
237
  banlist: [
238
- 'PU', 'Arctovish', 'Aurorus', 'Basculin', 'Centiskorch', 'Drampa', 'Exeggutor-Alola', 'Gallade', 'Haunter', 'Magmortar', 'Magneton', 'Malamar',
239
- 'Omastar', 'Rotom-Frost', 'Turtonator', 'Vanilluxe', 'Vikavolt', 'Silvally-Dragon', 'Silvally-Ground', 'Sneasel', 'Damp Rock', 'Grassy Seed',
238
+ 'PU', 'Arctovish', 'Aurorus', 'Basculin', 'Centiskorch', 'Drampa', 'Exeggutor-Alola', 'Gallade', 'Haunter', 'Magmortar', 'Magneton',
239
+ 'Malamar', 'Ninjask', 'Omastar', 'Rotom-Frost', 'Turtonator', 'Vanilluxe', 'Vikavolt', 'Silvally-Dragon', 'Silvally-Ground', 'Sneasel',
240
+ 'Damp Rock', 'Grassy Seed',
240
241
  ],
241
242
  },
242
243
  {
@@ -289,6 +290,17 @@ export const Formats: FormatList = [
289
290
  ruleset: ['Flat Rules', '!! Adjust Level = 50', 'Min Source Gen = 8', 'Limit Two Restricted'],
290
291
  restricted: ['Restricted Legendary'],
291
292
  },
293
+ {
294
+ name: "[Gen 8] I Choose 'Chu!",
295
+ threads: [
296
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3705481/">I Choose 'Chu! Discussion</a>`,
297
+ ],
298
+
299
+ mod: 'gen8',
300
+ ruleset: ['Flat Rules', '!! Adjust Level = 50', 'Min Source Gen = 8'],
301
+ banlist: ['All Pokemon', 'Raichu-Alola + Sing'],
302
+ unbanlist: ['Pichu', 'Pikachu', 'Raichu'],
303
+ },
292
304
  {
293
305
  name: "[Gen 8] Custom Game",
294
306
 
@@ -452,18 +464,6 @@ export const Formats: FormatList = [
452
464
  }
453
465
  },
454
466
  },
455
- {
456
- name: "[Gen 8] Jump! Magikarp!",
457
- desc: `Every team must contain Magikarp and bring it to the game.`,
458
- threads: [
459
- `&bullet; <a href="https://www.smogon.com/forums/threads/3704588/">Jump! Magikarp!</a>`,
460
- ],
461
-
462
- mod: 'gen8',
463
- gameType: 'doubles',
464
- ruleset: ['Flat Rules', '!! Picked Team Size = 2', '!! Adjust Level = 50', 'Min Source Gen = 8', 'Force Select = Magikarp'],
465
- banlist: ['Sub-Legendary'],
466
- },
467
467
  {
468
468
  name: "[Gen 8] Doubles Custom Game",
469
469
 
@@ -628,262 +628,42 @@ export const Formats: FormatList = [
628
628
  column: 2,
629
629
  },
630
630
  {
631
- name: "[Gen 8] Broken Record",
632
- desc: `Pok&eacute;mon can hold a TR to use that move in battle.`,
631
+ name: "[Gen 8] Re-Evolution",
632
+ desc: `Pok&eacute;mon gain the stat changes they would gain from evolving again.`,
633
633
  threads: [
634
- `&bullet; <a href="https://www.smogon.com/forums/threads/3701270/">Broken Record</a>`,
634
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3703643/">Re-Evolution</a>`,
635
635
  ],
636
636
 
637
637
  mod: 'gen8',
638
- ruleset: ['Standard', '!Sleep Clause Mod', 'Sleep Moves Clause', 'Dynamax Clause'],
638
+ ruleset: ['Standard', 'Re-Evolution Mod', 'Evasion Abilities Clause', 'Dynamax Clause'],
639
639
  banlist: [
640
- 'Calyrex-Ice', 'Calyrex-Shadow', 'Cinderace', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Dragapult', 'Eternatus', 'Genesect', 'Giratina',
641
- 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala',
642
- 'Magearna', 'Marshadow', 'Melmetal', 'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza',
643
- 'Regieleki', 'Reshiram', 'Rillaboom', 'Solgaleo', 'Spectrier', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta',
644
- 'Zamazenta-Crowned', 'Zekrom', 'Zygarde-Base', 'Arena Trap', 'Magnet Pull', 'Moody', 'Power Construct', 'Shadow Tag', 'TR29 (Baton Pass)',
645
- 'TR82 (Stored Power)', 'Baton Pass',
640
+ 'Darmanitan-Galar', 'Gyarados', 'Lunala', 'Milotic', 'Naganadel', 'Solgaleo', 'Urshifu-Base', 'Volcarona',
641
+ 'Zacian-Crowned', 'Arena Trap', 'Moody', 'Shadow Tag', 'Bright Powder', 'King\'s Rock', 'Lax Incense', 'Baton Pass',
646
642
  ],
647
- onValidateSet(set) {
648
- if (!set.item) return;
649
- const item = this.dex.items.get(set.item);
650
- if (!/^tr\d\d/i.test(item.name)) return;
651
- const moveName = item.desc.split('move ')[1].split('.')[0];
652
- if (set.moves.map(this.toID).includes(this.toID(moveName))) {
653
- return [
654
- `${set.species} can't run ${item.name} (${moveName}) as its item because it already has that move in its moveset.`,
655
- ];
656
- }
657
- },
658
- onValidateTeam(team) {
659
- const trs = new Set<string>();
660
- for (const set of team) {
661
- if (!set.item) continue;
662
- const item = this.dex.items.get(set.item).name;
663
- if (!/^tr\d\d/i.test(item)) continue;
664
- if (trs.has(item)) {
665
- return [`Your team already has a Pok\u00e9mon with ${item}.`];
666
- }
667
- trs.add(item);
668
- }
669
- },
670
- onTakeItem(item) {
671
- return !/^tr\d\d/i.test(item.name);
672
- },
673
- onModifyMove(move) {
674
- if (move.id === 'knockoff') {
675
- move.onBasePower = function (basePower, source, target, m) {
676
- const item = target.getItem();
677
- if (!this.singleEvent('TakeItem', item, target.itemState, target, target, m, item)) return;
678
- // Very hardcode but I'd prefer to not make a mod for one damage calculation change
679
- if (item.id && !/^tr\d\d/i.test(item.id)) {
680
- return this.chainModify(1.5);
681
- }
682
- };
683
- }
684
- },
685
- onBegin() {
686
- for (const pokemon of this.getAllPokemon()) {
687
- const item = pokemon.getItem();
688
- if (/^tr\d\d/i.test(item.name)) {
689
- const move = this.dex.moves.get(item.desc.split('move ')[1].split('.')[0]);
690
- pokemon.moveSlots = (pokemon as any).baseMoveSlots = [
691
- ...pokemon.baseMoveSlots, {
692
- id: move.id,
693
- move: move.name,
694
- pp: move.pp * 8 / 5,
695
- maxpp: move.pp * 8 / 5,
696
- target: move.target,
697
- disabled: false,
698
- disabledSource: '',
699
- used: false,
700
- },
701
- ];
702
- }
703
- }
704
- },
705
643
  },
706
644
  {
707
- name: "[Gen 8] Inheritance",
708
- desc: `Pok&eacute;mon may use the ability and moves of another, as long as they forfeit their own learnset.`,
645
+ name: "[Gen 8] Category Swap",
646
+ desc: `All physical moves become special, and all special moves become physical.`,
709
647
  threads: [
710
- `&bullet; <a href="https://www.smogon.com/forums/threads/3656811/">Inheritance</a>`,
648
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3702709/">Category Swap</a>`,
711
649
  ],
712
650
 
713
651
  mod: 'gen8',
714
- // searchShow: false,
715
- ruleset: ['Standard', '!Sleep Clause Mod', 'Sleep Moves Clause', '2 Ability Clause', 'Dynamax Clause'],
652
+ ruleset: ['Standard', 'Dynamax Clause'],
716
653
  banlist: [
717
- 'Blacephalon', 'Blaziken', 'Butterfree', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chansey', 'Combusken', 'Cresselia', 'Darmanitan-Galar', 'Dialga', 'Dracovish',
718
- 'Eternatus', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala',
719
- 'Magearna', 'Marshadow', 'Melmetal', 'Mewtwo', 'Natu', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza', 'Regieleki',
720
- 'Regigigas', 'Reshiram', 'Sableye', 'Shedinja', 'Solgaleo', 'Spectrier', 'Tapu Koko', 'Toxtricity', 'Torkoal', 'Urshifu-Base', 'Xatu', 'Xerneas', 'Yveltal',
721
- 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zeraora', 'Zekrom', 'Arena Trap', 'Contrary', 'Drizzle', 'Huge Power', 'Imposter', 'Innards Out',
722
- 'Libero', 'Moody', 'Power Construct', 'Pure Power', 'Quick Draw', 'Shadow Tag', 'Sheer Force', 'Simple', 'Unaware', 'Unburden', 'Water Bubble', 'King\'s Rock',
723
- 'Quick Claw', 'Baton Pass', 'Bolt Beak', 'Fishious Rend', 'Shell Smash', 'Thousand Arrows',
654
+ 'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina', 'Giratina-Origin', 'Groudon',
655
+ 'Ho-Oh', 'Kyogre', 'Kyurem', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala', 'Mewtwo', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane',
656
+ 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram', 'Solgaleo', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned',
657
+ 'Zekrom', 'Arena Trap', 'Moody', 'Power Construct', 'Shadow Tag', 'King\'s Rock', 'Baton Pass', 'Draco Meteor', 'Overheat',
724
658
  ],
725
- getEvoFamily(speciesid) {
726
- let species = Dex.species.get(speciesid);
727
- while (species.prevo) {
728
- species = Dex.species.get(species.prevo);
729
- }
730
- return species.id;
731
- },
732
- validateSet(set, teamHas) {
733
- const unreleased = (pokemon: Species) => pokemon.tier === "Unreleased" && pokemon.isNonstandard === "Unobtainable";
734
- if (!teamHas.abilityMap) {
735
- teamHas.abilityMap = Object.create(null);
736
- for (const pokemon of Dex.species.all()) {
737
- if (pokemon.isNonstandard || unreleased(pokemon)) continue;
738
- if (pokemon.requiredAbility || pokemon.requiredItem || pokemon.requiredMove) continue;
739
- if (this.ruleTable.isBannedSpecies(pokemon)) continue;
740
-
741
- for (const key of Object.values(pokemon.abilities)) {
742
- const abilityId = this.dex.toID(key);
743
- if (abilityId in teamHas.abilityMap) {
744
- teamHas.abilityMap[abilityId][pokemon.evos ? 'push' : 'unshift'](pokemon.id);
745
- } else {
746
- teamHas.abilityMap[abilityId] = [pokemon.id];
747
- }
748
- }
749
- }
750
- }
751
-
752
- const problem = this.validateForme(set);
753
- if (problem.length) return problem;
754
-
755
- const species = this.dex.species.get(set.species);
756
- if (!species.exists || species.num < 1) return [`The Pok\u00e9mon "${set.species}" does not exist.`];
757
- if (species.isNonstandard || unreleased(species)) {
758
- return [`${species.name} is not obtainable in Generation ${this.dex.gen}.`];
759
- }
760
-
761
- const name = set.name;
762
- if (this.ruleTable.isBannedSpecies(species)) {
763
- return this.validateSet(set, teamHas);
764
- }
765
-
766
- const ability = this.dex.abilities.get(set.ability);
767
- if (!ability.exists || ability.isNonstandard) return [`${name} needs to have a valid ability.`];
768
- const pokemonWithAbility = teamHas.abilityMap[ability.id];
769
- if (!pokemonWithAbility) return [`${ability.name} is not available on a legal Pok\u00e9mon.`];
770
-
771
- (this.format as any).debug = true;
772
-
773
- if (!teamHas.abilitySources) teamHas.abilitySources = Object.create(null);
774
- const validSources: string[] = teamHas.abilitySources[this.dex.toID(set.species)] = []; // Evolution families
775
-
776
- let canonicalSource = ''; // Specific for the basic implementation of Donor Clause (see onValidateTeam).
777
-
778
- for (const donor of pokemonWithAbility) {
779
- const donorSpecies = this.dex.species.get(donor);
780
- let format = this.format;
781
- if (!format.getEvoFamily) format = this.dex.formats.get('gen8inheritance');
782
- const evoFamily = format.getEvoFamily!(donorSpecies.id);
783
- if (validSources.includes(evoFamily)) continue;
784
-
785
- set.species = donorSpecies.name;
786
- set.name = donorSpecies.baseSpecies;
787
- const problems = this.validateSet(set, teamHas) || [];
788
- if (!problems.length) {
789
- validSources.push(evoFamily);
790
- canonicalSource = donorSpecies.name;
791
- }
792
- // Specific for the basic implementation of Donor Clause (see onValidateTeam).
793
- if (validSources.length > 1) break;
794
- }
795
- (this.format as any).debug = false;
796
-
797
- set.name = name;
798
- set.species = species.name;
799
- if (!validSources.length) {
800
- if (pokemonWithAbility.length > 1) return [`${name}'s set is illegal.`];
801
- return [`${name} has an illegal set with an ability from ${this.dex.species.get(pokemonWithAbility[0]).name}.`];
802
- }
803
-
804
- // Protocol: Include the data of the donor species in the `ability` data slot.
805
- // Afterwards, we are going to reset the name to what the user intended.
806
- set.ability = `${set.ability}0${canonicalSource}`;
807
- return null;
808
- },
809
- onValidateTeam(team, f, teamHas) {
810
- if (this.ruleTable.has('2abilityclause')) {
811
- const abilityTable = new Map<string, number>();
812
- const base: {[k: string]: string} = {
813
- airlock: 'cloudnine',
814
- battlearmor: 'shellarmor',
815
- clearbody: 'whitesmoke',
816
- dazzling: 'queenlymajesty',
817
- emergencyexit: 'wimpout',
818
- filter: 'solidrock',
819
- gooey: 'tanglinghair',
820
- insomnia: 'vitalspirit',
821
- ironbarbs: 'roughskin',
822
- libero: 'protean',
823
- minus: 'plus',
824
- moxie: 'chillingneigh',
825
- powerofalchemy: 'receiver',
826
- propellertail: 'stalwart',
827
- teravolt: 'moldbreaker',
828
- turboblaze: 'moldbreaker',
829
- };
830
- for (const set of team) {
831
- let ability = this.toID(set.ability.split('0')[0]);
832
- if (!ability) continue;
833
- if (ability in base) ability = base[ability] as ID;
834
- if ((abilityTable.get(ability) || 0) >= 2) {
835
- return [
836
- `You are limited to two of each ability by 2 Ability Clause.`,
837
- `(You have more than two ${this.dex.abilities.get(ability).name} variants)`,
838
- ];
839
- }
840
- abilityTable.set(ability, (abilityTable.get(ability) || 0) + 1);
841
- }
842
- }
843
-
844
- // Donor Clause
845
- const evoFamilyLists = [];
846
- for (const set of team) {
847
- const abilitySources = teamHas.abilitySources?.[this.dex.toID(set.species)];
848
- if (!abilitySources) continue;
849
- let format = this.format;
850
- if (!format.getEvoFamily) format = this.dex.formats.get('gen8inheritance');
851
- evoFamilyLists.push(abilitySources.map(format.getEvoFamily!));
852
- }
853
-
854
- // Checking actual full incompatibility would require expensive algebra.
855
- // Instead, we only check the trivial case of multiple Pokémon only legal for exactly one family. FIXME?
856
- const requiredFamilies = Object.create(null);
857
- for (const evoFamilies of evoFamilyLists) {
858
- if (evoFamilies.length !== 1) continue;
859
- const [familyId] = evoFamilies;
860
- if (!(familyId in requiredFamilies)) requiredFamilies[familyId] = 1;
861
- requiredFamilies[familyId]++;
862
- if (requiredFamilies[familyId] > 2) {
863
- return [
864
- `You are limited to up to two inheritances from each evolution family by the Donor Clause.`,
865
- `(You inherit more than twice from ${this.dex.species.get(familyId).name}).`,
866
- ];
867
- }
868
- }
869
- },
870
- onBegin() {
871
- for (const pokemon of this.getAllPokemon()) {
872
- if (pokemon.baseAbility.includes('0')) {
873
- const donor = pokemon.baseAbility.split('0')[1];
874
- pokemon.m.donor = this.toID(donor);
875
- pokemon.baseAbility = this.toID(pokemon.baseAbility.split('0')[0]);
876
- pokemon.ability = pokemon.baseAbility;
877
- }
659
+ onModifyMove(move) {
660
+ if (move.category === 'Status') return;
661
+ if (move.category === 'Special') {
662
+ move.category = 'Physical';
663
+ } else {
664
+ move.category = 'Special';
878
665
  }
879
666
  },
880
- onSwitchIn(pokemon) {
881
- if (!pokemon.m.donor) return;
882
- const donorTemplate = this.dex.species.get(pokemon.m.donor);
883
- if (!donorTemplate.exists) return;
884
- // Place volatiles on the Pokémon to show the donor details.
885
- this.add('-start', pokemon, donorTemplate.name, '[silent]');
886
- },
887
667
  },
888
668
 
889
669
  // Other Metagames
@@ -1050,8 +830,8 @@ export const Formats: FormatList = [
1050
830
  mod: 'gen8',
1051
831
  ruleset: ['Standard', 'Dynamax Clause', 'Sleep Moves Clause'],
1052
832
  banlist: [
1053
- 'Blissey', 'Calyrex-Shadow', 'Chansey', 'Crawdaunt', 'Dragapult', 'Eternatus', 'Hawlucha', 'Marowak-Alola', 'Melmetal',
1054
- 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', 'Uber > 1', 'AG ++ Uber > 1', 'Arena Trap', 'Huge Power',
833
+ 'Blissey', 'Calyrex-Shadow', 'Chansey', 'Crawdaunt', 'Dragapult', 'Eternatus', 'Hawlucha', 'Marowak-Alola', 'Melmetal', 'Nidoking',
834
+ 'Nidoqueen', 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', 'Uber > 1', 'AG ++ Uber > 1', 'Arena Trap', 'Huge Power',
1055
835
  'Moody', 'Pure Power', 'Shadow Tag', 'Swift Swim', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense', 'Quick Claw',
1056
836
  'Baton Pass',
1057
837
  ],
@@ -1269,17 +1049,6 @@ export const Formats: FormatList = [
1269
1049
  gameType: 'doubles',
1270
1050
  ruleset: ['Flat Rules', 'Min Source Gen = 8'],
1271
1051
  },
1272
- {
1273
- name: "[Gen 8 BDSP] Pure Hackmons",
1274
- desc: `Anything that can be hacked in-game and is usable in local battles is allowed.`,
1275
- threads: [
1276
- `&bullet; <a href="https://www.smogon.com/forums/threads/3693868/">Pure Hackmons</a>`,
1277
- ],
1278
-
1279
- mod: 'gen8bdsp',
1280
- searchShow: false,
1281
- ruleset: ['-Nonexistent', 'Team Preview', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause'],
1282
- },
1283
1052
  {
1284
1053
  section: "Challengeable OMs",
1285
1054
  column: 2,
@@ -1439,6 +1208,189 @@ export const Formats: FormatList = [
1439
1208
  }
1440
1209
  },
1441
1210
  },
1211
+ {
1212
+ name: "[Gen 8] Inheritance",
1213
+ desc: `Pok&eacute;mon may use the ability and moves of another, as long as they forfeit their own learnset.`,
1214
+ threads: [
1215
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3656811/">Inheritance</a>`,
1216
+ ],
1217
+
1218
+ mod: 'gen8',
1219
+ searchShow: false,
1220
+ ruleset: ['Standard', '!Sleep Clause Mod', 'Sleep Moves Clause', '2 Ability Clause', 'Dynamax Clause'],
1221
+ banlist: [
1222
+ 'Blacephalon', 'Blaziken', 'Butterfree', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chansey', 'Combusken', 'Cresselia', 'Darmanitan-Galar', 'Dialga',
1223
+ 'Dracovish', 'Eternatus', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh', 'Kartana', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Lugia',
1224
+ 'Lunala', 'Magearna', 'Marshadow', 'Melmetal', 'Mewtwo', 'Natu', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza',
1225
+ 'Regieleki', 'Regigigas', 'Reshiram', 'Sableye', 'Shedinja', 'Solgaleo', 'Spectrier', 'Tapu Koko', 'Toxtricity', 'Torkoal', 'Urshifu-Base',
1226
+ 'Xatu', 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zeraora', 'Zekrom', 'Arena Trap', 'Contrary',
1227
+ 'Drizzle', 'Huge Power', 'Imposter', 'Innards Out', 'Libero', 'Moody', 'Power Construct', 'Pure Power', 'Quick Draw', 'Shadow Tag', 'Sheer Force',
1228
+ 'Simple', 'Unaware', 'Unburden', 'Water Bubble', 'King\'s Rock', 'Quick Claw', 'Baton Pass', 'Bolt Beak', 'Fishious Rend', 'Shell Smash',
1229
+ 'Thousand Arrows',
1230
+ ],
1231
+ getEvoFamily(speciesid) {
1232
+ let species = Dex.species.get(speciesid);
1233
+ while (species.prevo) {
1234
+ species = Dex.species.get(species.prevo);
1235
+ }
1236
+ return species.id;
1237
+ },
1238
+ validateSet(set, teamHas) {
1239
+ const unreleased = (pokemon: Species) => pokemon.tier === "Unreleased" && pokemon.isNonstandard === "Unobtainable";
1240
+ if (!teamHas.abilityMap) {
1241
+ teamHas.abilityMap = Object.create(null);
1242
+ for (const pokemon of Dex.species.all()) {
1243
+ if (pokemon.isNonstandard || unreleased(pokemon)) continue;
1244
+ if (pokemon.requiredAbility || pokemon.requiredItem || pokemon.requiredMove) continue;
1245
+ if (this.ruleTable.isBannedSpecies(pokemon)) continue;
1246
+
1247
+ for (const key of Object.values(pokemon.abilities)) {
1248
+ const abilityId = this.dex.toID(key);
1249
+ if (abilityId in teamHas.abilityMap) {
1250
+ teamHas.abilityMap[abilityId][pokemon.evos ? 'push' : 'unshift'](pokemon.id);
1251
+ } else {
1252
+ teamHas.abilityMap[abilityId] = [pokemon.id];
1253
+ }
1254
+ }
1255
+ }
1256
+ }
1257
+
1258
+ const problem = this.validateForme(set);
1259
+ if (problem.length) return problem;
1260
+
1261
+ const species = this.dex.species.get(set.species);
1262
+ if (!species.exists || species.num < 1) return [`The Pok\u00e9mon "${set.species}" does not exist.`];
1263
+ if (species.isNonstandard || unreleased(species)) {
1264
+ return [`${species.name} is not obtainable in Generation ${this.dex.gen}.`];
1265
+ }
1266
+
1267
+ const name = set.name;
1268
+ if (this.ruleTable.isBannedSpecies(species)) {
1269
+ return this.validateSet(set, teamHas);
1270
+ }
1271
+
1272
+ const ability = this.dex.abilities.get(set.ability);
1273
+ if (!ability.exists || ability.isNonstandard) return [`${name} needs to have a valid ability.`];
1274
+ const pokemonWithAbility = teamHas.abilityMap[ability.id];
1275
+ if (!pokemonWithAbility) return [`${ability.name} is not available on a legal Pok\u00e9mon.`];
1276
+
1277
+ (this.format as any).debug = true;
1278
+
1279
+ if (!teamHas.abilitySources) teamHas.abilitySources = Object.create(null);
1280
+ const validSources: string[] = teamHas.abilitySources[this.dex.toID(set.species)] = []; // Evolution families
1281
+
1282
+ let canonicalSource = ''; // Specific for the basic implementation of Donor Clause (see onValidateTeam).
1283
+
1284
+ for (const donor of pokemonWithAbility) {
1285
+ const donorSpecies = this.dex.species.get(donor);
1286
+ let format = this.format;
1287
+ if (!format.getEvoFamily) format = this.dex.formats.get('gen8inheritance');
1288
+ const evoFamily = format.getEvoFamily!(donorSpecies.id);
1289
+ if (validSources.includes(evoFamily)) continue;
1290
+
1291
+ set.species = donorSpecies.name;
1292
+ set.name = donorSpecies.baseSpecies;
1293
+ const problems = this.validateSet(set, teamHas) || [];
1294
+ if (!problems.length) {
1295
+ validSources.push(evoFamily);
1296
+ canonicalSource = donorSpecies.name;
1297
+ }
1298
+ // Specific for the basic implementation of Donor Clause (see onValidateTeam).
1299
+ if (validSources.length > 1) break;
1300
+ }
1301
+ (this.format as any).debug = false;
1302
+
1303
+ set.name = name;
1304
+ set.species = species.name;
1305
+ if (!validSources.length) {
1306
+ if (pokemonWithAbility.length > 1) return [`${name}'s set is illegal.`];
1307
+ return [`${name} has an illegal set with an ability from ${this.dex.species.get(pokemonWithAbility[0]).name}.`];
1308
+ }
1309
+
1310
+ // Protocol: Include the data of the donor species in the `ability` data slot.
1311
+ // Afterwards, we are going to reset the name to what the user intended.
1312
+ set.ability = `${set.ability}0${canonicalSource}`;
1313
+ return null;
1314
+ },
1315
+ onValidateTeam(team, f, teamHas) {
1316
+ if (this.ruleTable.has('2abilityclause')) {
1317
+ const abilityTable = new Map<string, number>();
1318
+ const base: {[k: string]: string} = {
1319
+ airlock: 'cloudnine',
1320
+ battlearmor: 'shellarmor',
1321
+ clearbody: 'whitesmoke',
1322
+ dazzling: 'queenlymajesty',
1323
+ emergencyexit: 'wimpout',
1324
+ filter: 'solidrock',
1325
+ gooey: 'tanglinghair',
1326
+ insomnia: 'vitalspirit',
1327
+ ironbarbs: 'roughskin',
1328
+ libero: 'protean',
1329
+ minus: 'plus',
1330
+ moxie: 'chillingneigh',
1331
+ powerofalchemy: 'receiver',
1332
+ propellertail: 'stalwart',
1333
+ teravolt: 'moldbreaker',
1334
+ turboblaze: 'moldbreaker',
1335
+ };
1336
+ for (const set of team) {
1337
+ let ability = this.toID(set.ability.split('0')[0]);
1338
+ if (!ability) continue;
1339
+ if (ability in base) ability = base[ability] as ID;
1340
+ if ((abilityTable.get(ability) || 0) >= 2) {
1341
+ return [
1342
+ `You are limited to two of each ability by 2 Ability Clause.`,
1343
+ `(You have more than two ${this.dex.abilities.get(ability).name} variants)`,
1344
+ ];
1345
+ }
1346
+ abilityTable.set(ability, (abilityTable.get(ability) || 0) + 1);
1347
+ }
1348
+ }
1349
+
1350
+ // Donor Clause
1351
+ const evoFamilyLists = [];
1352
+ for (const set of team) {
1353
+ const abilitySources = teamHas.abilitySources?.[this.dex.toID(set.species)];
1354
+ if (!abilitySources) continue;
1355
+ let format = this.format;
1356
+ if (!format.getEvoFamily) format = this.dex.formats.get('gen8inheritance');
1357
+ evoFamilyLists.push(abilitySources.map(format.getEvoFamily!));
1358
+ }
1359
+
1360
+ // Checking actual full incompatibility would require expensive algebra.
1361
+ // Instead, we only check the trivial case of multiple Pokémon only legal for exactly one family. FIXME?
1362
+ const requiredFamilies = Object.create(null);
1363
+ for (const evoFamilies of evoFamilyLists) {
1364
+ if (evoFamilies.length !== 1) continue;
1365
+ const [familyId] = evoFamilies;
1366
+ if (!(familyId in requiredFamilies)) requiredFamilies[familyId] = 1;
1367
+ requiredFamilies[familyId]++;
1368
+ if (requiredFamilies[familyId] > 2) {
1369
+ return [
1370
+ `You are limited to up to two inheritances from each evolution family by the Donor Clause.`,
1371
+ `(You inherit more than twice from ${this.dex.species.get(familyId).name}).`,
1372
+ ];
1373
+ }
1374
+ }
1375
+ },
1376
+ onBegin() {
1377
+ for (const pokemon of this.getAllPokemon()) {
1378
+ if (pokemon.baseAbility.includes('0')) {
1379
+ const donor = pokemon.baseAbility.split('0')[1];
1380
+ pokemon.m.donor = this.toID(donor);
1381
+ pokemon.baseAbility = this.toID(pokemon.baseAbility.split('0')[0]);
1382
+ pokemon.ability = pokemon.baseAbility;
1383
+ }
1384
+ }
1385
+ },
1386
+ onSwitchIn(pokemon) {
1387
+ if (!pokemon.m.donor) return;
1388
+ const donorTemplate = this.dex.species.get(pokemon.m.donor);
1389
+ if (!donorTemplate.exists) return;
1390
+ // Place volatiles on the Pokémon to show the donor details.
1391
+ this.add('-start', pokemon, donorTemplate.name, '[silent]');
1392
+ },
1393
+ },
1442
1394
  {
1443
1395
  name: "[Gen 8] Linked",
1444
1396
  desc: `The first two moves in a Pok&eacute;mon's moveset are used simultaneously.`,
@@ -1790,6 +1742,17 @@ export const Formats: FormatList = [
1790
1742
  pokemon.m.innates = undefined;
1791
1743
  },
1792
1744
  },
1745
+ {
1746
+ name: "[Gen 8] Pure Hackmons",
1747
+ desc: `Anything that can be hacked in-game and is usable in local battles is allowed.`,
1748
+ threads: [
1749
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3656851/">Pure Hackmons</a>`,
1750
+ ],
1751
+
1752
+ mod: 'gen8',
1753
+ searchShow: false,
1754
+ ruleset: ['-Nonexistent', 'Team Preview', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause'],
1755
+ },
1793
1756
  {
1794
1757
  name: "[Gen 8] Shared Power",
1795
1758
  desc: `Once a Pok&eacute;mon switches in, its ability is shared with the rest of the team.`,
@@ -2438,47 +2401,39 @@ export const Formats: FormatList = [
2438
2401
  column: 3,
2439
2402
  },
2440
2403
  {
2441
- name: "[Gen 2] Ubers",
2404
+ name: "[Gen 3] Ubers",
2442
2405
  threads: [
2443
- `&bullet; <a href="https://www.smogon.com/forums/posts/8286282/">GSC Ubers</a>`,
2406
+ `&bullet; <a href="https://www.smogon.com/forums/posts/8286280/">ADV Ubers</a>`,
2444
2407
  ],
2445
2408
 
2446
- mod: 'gen2',
2409
+ mod: 'gen3',
2447
2410
  // searchShow: false,
2448
- ruleset: ['Standard'],
2411
+ ruleset: ['Standard', 'Deoxys Camouflage Clause', 'One Baton Pass Clause'],
2412
+ banlist: ['Wobbuffet + Leftovers'],
2449
2413
  },
2450
2414
  {
2451
- name: "[Gen 3] 1v1",
2452
- desc: `Bring three Pok&eacute;mon to Team Preview and choose one to battle.`,
2415
+ name: "[Gen 5] RU",
2453
2416
  threads: [
2454
- `&bullet; <a href="https://www.smogon.com/forums/posts/8031456/">ADV 1v1</a>`,
2417
+ `&bullet; <a href="https://www.smogon.com/forums/posts/6431094/">BW2 Sample Teams</a>`,
2418
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3473124/">BW2 RU Viability Rankings</a>`,
2455
2419
  ],
2456
2420
 
2457
- mod: 'gen3',
2421
+ mod: 'gen5',
2458
2422
  // searchShow: false,
2459
- ruleset: [
2460
- 'Picked Team Size = 1', 'Max Team Size = 3',
2461
- '[Gen 3] OU', 'Accuracy Moves Clause', 'Sleep Moves Clause', 'Team Preview', '!Freeze Clause Mod',
2462
- ],
2463
- banlist: [
2464
- 'Clefable', 'Slaking', 'Snorlax', 'Suicune', 'Zapdos', 'Destiny Bond', 'Explosion', 'Ingrain', 'Perish Song',
2465
- 'Self-Destruct', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense', 'Quick Claw',
2466
- ],
2467
- unbanlist: ['Mr. Mime', 'Wobbuffet', 'Wynaut', 'Sand Veil'],
2423
+ ruleset: ['[Gen 5] UU', 'Baton Pass Clause', '!Sleep Clause Mod', 'Sleep Moves Clause'],
2424
+ banlist: ['UU', 'RUBL', 'Shadow Tag', 'Shell Smash + Baton Pass'],
2425
+ unbanlist: ['Prankster + Assist', 'Prankster + Copycat', 'Baton Pass'],
2468
2426
  },
2469
2427
  {
2470
- name: "[Gen 1] Stadium OU",
2428
+ name: "[Gen 7 Let's Go] UU",
2471
2429
  threads: [
2472
- `&bullet; <a href="https://www.smogon.com/forums/threads/3685877/">Stadium OU Viability Rankings</a>`,
2430
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3659882/">LGPE UU Metagame Discussion &amp; Resources</a>`,
2473
2431
  ],
2474
2432
 
2475
- mod: 'gen1stadium',
2433
+ mod: 'gen7letsgo',
2476
2434
  // searchShow: false,
2477
- ruleset: ['Standard', 'Team Preview'],
2478
- banlist: ['Uber',
2479
- 'Nidoking + Fury Attack + Thrash', 'Exeggutor + Poison Powder + Stomp', 'Exeggutor + Sleep Powder + Stomp',
2480
- 'Exeggutor + Stun Spore + Stomp', 'Jolteon + Focus Energy + Thunder Shock', 'Flareon + Focus Energy + Ember',
2481
- ],
2435
+ ruleset: ['[Gen 7 Let\'s Go] OU'],
2436
+ banlist: ['OU', 'UUBL'],
2482
2437
  },
2483
2438
 
2484
2439
  // Past Gens OU
@@ -3329,19 +3284,6 @@ export const Formats: FormatList = [
3329
3284
  ruleset: ['Standard', 'Evasion Abilities Clause', 'Swagger Clause', 'Sleep Clause Mod'],
3330
3285
  banlist: ['Uber', 'OU', 'UUBL', 'Arena Trap', 'Drought', 'Sand Stream', 'Snow Warning', 'Prankster + Assist', 'Prankster + Copycat', 'Baton Pass'],
3331
3286
  },
3332
- {
3333
- name: "[Gen 5] RU",
3334
- threads: [
3335
- `&bullet; <a href="https://www.smogon.com/forums/posts/6431094/">BW2 Sample Teams</a>`,
3336
- `&bullet; <a href="https://www.smogon.com/forums/threads/3473124/">BW2 RU Viability Rankings</a>`,
3337
- ],
3338
-
3339
- mod: 'gen5',
3340
- searchShow: false,
3341
- ruleset: ['[Gen 5] UU', 'Baton Pass Clause', '!Sleep Clause Mod', 'Sleep Moves Clause'],
3342
- banlist: ['UU', 'RUBL', 'Shadow Tag', 'Shell Smash + Baton Pass'],
3343
- unbanlist: ['Prankster + Assist', 'Prankster + Copycat', 'Baton Pass'],
3344
- },
3345
3287
  {
3346
3288
  name: "[Gen 5] NU",
3347
3289
  threads: [
@@ -3417,6 +3359,7 @@ export const Formats: FormatList = [
3417
3359
  searchShow: false,
3418
3360
  ruleset: ['[Gen 5] PU'],
3419
3361
  banlist: ['PU', 'Articuno', 'Dragonair', 'Glalie', 'Machoke', 'Marowak', 'Omanyte', 'Regigigas', 'Trubbish', 'Whirlipede', 'Baton Pass'],
3362
+ unbanlist: ['Damp Rock'],
3420
3363
  },
3421
3364
  {
3422
3365
  name: "[Gen 5] GBU Singles",
@@ -3618,7 +3561,7 @@ export const Formats: FormatList = [
3618
3561
  ruleset: ['[Gen 4] PU'],
3619
3562
  banlist: [
3620
3563
  'Ampharos', 'Armaldo', 'Bellossom', 'Dragonair', 'Electabuzz', 'Gabite', 'Gastrodon', 'Glaceon', 'Glalie',
3621
- 'Golduck', 'Gorebyss', 'Hippopotas', 'Kadabra', 'Machoke', 'Magmar', 'Mantine', 'Marowak', 'Metang',
3564
+ 'Golduck', 'Gorebyss', 'Hippopotas', 'Kadabra', 'Lapras', 'Machoke', 'Magmar', 'Mantine', 'Marowak', 'Metang',
3622
3565
  'Misdreavus', 'Monferno', 'Mr. Mime', 'Muk', 'Murkrow', 'Pinsir', 'Politoed', 'Purugly', 'Quagsire',
3623
3566
  'Raichu', 'Rampardos', 'Rapidash', 'Regigigas', 'Relicanth', 'Rhydon', 'Scyther', 'Sneasel', 'Snover',
3624
3567
  'Solrock', 'Tangela', 'Torkoal', 'Victreebel', 'Xatu', 'Zangoose', 'Damp Rock',
@@ -3691,17 +3634,6 @@ export const Formats: FormatList = [
3691
3634
  section: "Past Generations",
3692
3635
  column: 5,
3693
3636
  },
3694
- {
3695
- name: "[Gen 3] Ubers",
3696
- threads: [
3697
- `&bullet; <a href="https://www.smogon.com/forums/posts/8286280/">ADV Ubers</a>`,
3698
- ],
3699
-
3700
- mod: 'gen3',
3701
- searchShow: false,
3702
- ruleset: ['Standard', 'Deoxys Camouflage Clause', 'One Baton Pass Clause'],
3703
- banlist: ['Wobbuffet + Leftovers'],
3704
- },
3705
3637
  {
3706
3638
  name: "[Gen 3] UU",
3707
3639
  threads: [
@@ -3725,6 +3657,25 @@ export const Formats: FormatList = [
3725
3657
  ruleset: ['Standard'],
3726
3658
  banlist: ['Uber', 'OU', 'UUBL', 'UU', 'Smeargle + Ingrain'],
3727
3659
  },
3660
+ {
3661
+ name: "[Gen 3] 1v1",
3662
+ desc: `Bring three Pok&eacute;mon to Team Preview and choose one to battle.`,
3663
+ threads: [
3664
+ `&bullet; <a href="https://www.smogon.com/forums/posts/8031456/">ADV 1v1</a>`,
3665
+ ],
3666
+
3667
+ mod: 'gen3',
3668
+ searchShow: false,
3669
+ ruleset: [
3670
+ 'Picked Team Size = 1', 'Max Team Size = 3',
3671
+ '[Gen 3] OU', 'Accuracy Moves Clause', 'Sleep Moves Clause', 'Team Preview', '!Freeze Clause Mod',
3672
+ ],
3673
+ banlist: [
3674
+ 'Clefable', 'Slaking', 'Snorlax', 'Suicune', 'Zapdos', 'Destiny Bond', 'Explosion', 'Ingrain', 'Perish Song',
3675
+ 'Self-Destruct', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense', 'Quick Claw',
3676
+ ],
3677
+ unbanlist: ['Mr. Mime', 'Wobbuffet', 'Wynaut', 'Sand Veil'],
3678
+ },
3728
3679
  {
3729
3680
  name: "[Gen 3] Doubles OU",
3730
3681
  threads: [
@@ -3756,6 +3707,16 @@ export const Formats: FormatList = [
3756
3707
  debug: true,
3757
3708
  ruleset: ['HP Percentage Mod', 'Cancel Mod', 'Max Team Size = 24', 'Max Move Count = 24', 'Max Level = 9999', 'Default Level = 100'],
3758
3709
  },
3710
+ {
3711
+ name: "[Gen 2] Ubers",
3712
+ threads: [
3713
+ `&bullet; <a href="https://www.smogon.com/forums/posts/8286282/">GSC Ubers</a>`,
3714
+ ],
3715
+
3716
+ mod: 'gen2',
3717
+ searchShow: false,
3718
+ ruleset: ['Standard'],
3719
+ },
3759
3720
  {
3760
3721
  name: "[Gen 2] UU",
3761
3722
  threads: [`&bullet; <a href="https://www.smogon.com/forums/threads/3576710/">GSC UU</a>`],
@@ -3764,6 +3725,7 @@ export const Formats: FormatList = [
3764
3725
  searchShow: false,
3765
3726
  ruleset: ['[Gen 2] OU'],
3766
3727
  banlist: ['OU', 'UUBL'],
3728
+ unbanlist: ['Mean Look + Baton Pass', 'Spider Web + Baton Pass'],
3767
3729
  },
3768
3730
  {
3769
3731
  name: "[Gen 2] NU",
@@ -3854,7 +3816,7 @@ export const Formats: FormatList = [
3854
3816
 
3855
3817
  mod: 'gen1',
3856
3818
  searchShow: false,
3857
- ruleset: ['[Gen 1] UU'],
3819
+ ruleset: ['[Gen 1] UU', '!APT Clause'],
3858
3820
  banlist: ['UU', 'NUBL'],
3859
3821
  },
3860
3822
  {
@@ -3880,6 +3842,20 @@ export const Formats: FormatList = [
3880
3842
  ],
3881
3843
  banlist: ['Uber'],
3882
3844
  },
3845
+ {
3846
+ name: "[Gen 1] Stadium OU",
3847
+ threads: [
3848
+ `&bullet; <a href="https://www.smogon.com/forums/threads/3685877/">Stadium OU Viability Rankings</a>`,
3849
+ ],
3850
+
3851
+ mod: 'gen1stadium',
3852
+ searchShow: false,
3853
+ ruleset: ['Standard', 'Team Preview'],
3854
+ banlist: ['Uber',
3855
+ 'Nidoking + Fury Attack + Thrash', 'Exeggutor + Poison Powder + Stomp', 'Exeggutor + Sleep Powder + Stomp',
3856
+ 'Exeggutor + Stun Spore + Stomp', 'Jolteon + Focus Energy + Thunder Shock', 'Flareon + Focus Energy + Ember',
3857
+ ],
3858
+ },
3883
3859
  {
3884
3860
  name: "[Gen 1] Tradebacks OU",
3885
3861
  desc: `RBY OU with movepool additions from the Time Capsule.`,