@pkmn/sim 0.4.19 → 0.4.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 (88) hide show
  1. package/build/config/formats.js +752 -743
  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 +4 -1
  6. package/build/data/conditions.js.map +1 -1
  7. package/build/data/formats-data.js +27 -27
  8. package/build/data/formats-data.js.map +1 -1
  9. package/build/data/learnsets.js +10 -10
  10. package/build/data/learnsets.js.map +1 -1
  11. package/build/data/mods/gen1/moves.js +1 -1
  12. package/build/data/mods/gen1/moves.js.map +1 -1
  13. package/build/data/mods/gen1/scripts.js +18 -22
  14. package/build/data/mods/gen1/scripts.js.map +1 -1
  15. package/build/data/mods/gen2/scripts.js +16 -23
  16. package/build/data/mods/gen2/scripts.js.map +1 -1
  17. package/build/data/mods/gen3/moves.js +2 -1
  18. package/build/data/mods/gen3/moves.js.map +1 -1
  19. package/build/data/mods/gen4/conditions.js +6 -0
  20. package/build/data/mods/gen4/conditions.js.map +1 -1
  21. package/build/data/mods/gen4/moves.js +2 -1
  22. package/build/data/mods/gen4/moves.js.map +1 -1
  23. package/build/data/mods/gen4/scripts.js +2 -1
  24. package/build/data/mods/gen4/scripts.js.map +1 -1
  25. package/build/data/mods/gen6/conditions.js +4 -1
  26. package/build/data/mods/gen6/conditions.js.map +1 -1
  27. package/build/data/mods/gen7/formats-data.js +2 -2
  28. package/build/data/mods/gen7/formats-data.js.map +1 -1
  29. package/build/data/mods/gen7/moves.js +8 -0
  30. package/build/data/mods/gen7/moves.js.map +1 -1
  31. package/build/data/moves.js +26 -14
  32. package/build/data/moves.js.map +1 -1
  33. package/build/data/rulesets.js +258 -16
  34. package/build/data/rulesets.js.map +1 -1
  35. package/build/data/tags.js +3 -3
  36. package/build/data/tags.js.map +1 -1
  37. package/build/lib/streams.d.ts +1 -199
  38. package/build/lib/streams.js +11 -772
  39. package/build/lib/streams.js.map +1 -1
  40. package/build/sim/battle-actions.d.ts +1 -1
  41. package/build/sim/battle-actions.js +18 -42
  42. package/build/sim/battle-actions.js.map +1 -1
  43. package/build/sim/battle.d.ts +1 -0
  44. package/build/sim/battle.js +5 -0
  45. package/build/sim/battle.js.map +1 -1
  46. package/build/sim/dex-moves.d.ts +31 -11
  47. package/build/sim/dex-moves.js +4 -3
  48. package/build/sim/dex-moves.js.map +1 -1
  49. package/build/sim/dex-species.js +11 -1
  50. package/build/sim/dex-species.js.map +1 -1
  51. package/build/sim/exported-global-types.d.ts +4 -0
  52. package/build/sim/global-types.d.ts +4 -0
  53. package/build/sim/pokemon.js +1 -1
  54. package/build/sim/pokemon.js.map +1 -1
  55. package/build/sim/side.js +2 -2
  56. package/build/sim/side.js.map +1 -1
  57. package/build/sim/team-validator.d.ts +4 -0
  58. package/build/sim/team-validator.js +37 -11
  59. package/build/sim/team-validator.js.map +1 -1
  60. package/config/formats.ts +649 -630
  61. package/data/aliases.ts +4 -4
  62. package/data/conditions.ts +4 -1
  63. package/data/formats-data.ts +27 -27
  64. package/data/learnsets.ts +10 -10
  65. package/data/mods/gen1/moves.ts +1 -1
  66. package/data/mods/gen1/scripts.ts +23 -19
  67. package/data/mods/gen2/scripts.ts +20 -19
  68. package/data/mods/gen3/moves.ts +2 -1
  69. package/data/mods/gen4/conditions.ts +6 -0
  70. package/data/mods/gen4/moves.ts +2 -1
  71. package/data/mods/gen4/scripts.ts +1 -1
  72. package/data/mods/gen6/conditions.ts +4 -1
  73. package/data/mods/gen7/formats-data.ts +2 -2
  74. package/data/mods/gen7/moves.ts +8 -0
  75. package/data/moves.ts +23 -14
  76. package/data/rulesets.ts +235 -16
  77. package/data/tags.ts +3 -3
  78. package/lib/streams.ts +1 -874
  79. package/package.json +4 -3
  80. package/sim/battle-actions.ts +19 -40
  81. package/sim/battle.ts +6 -0
  82. package/sim/dex-moves.ts +35 -13
  83. package/sim/dex-species.ts +10 -1
  84. package/sim/exported-global-types.ts +4 -0
  85. package/sim/global-types.ts +4 -0
  86. package/sim/pokemon.ts +1 -1
  87. package/sim/side.ts +2 -2
  88. package/sim/team-validator.ts +41 -10
@@ -8,9 +8,12 @@ export const Conditions: {[k: string]: ModdedConditionData} = {
8
8
  par: {
9
9
  inherit: true,
10
10
  onModifySpe(spe, pokemon) {
11
+ // Paralysis occurs after all other Speed modifiers, so evaluate all modifiers up to this point first
12
+ spe = this.finalModify(spe);
11
13
  if (!pokemon.hasAbility('quickfeet')) {
12
- return this.chainModify(0.25);
14
+ spe = Math.floor(spe * 25 / 100);
13
15
  }
16
+ return spe;
14
17
  },
15
18
  },
16
19
  confusion: {
@@ -529,7 +529,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
529
529
  doublesTier: "(DUU)",
530
530
  },
531
531
  farfetchd: {
532
- randomBattleMoves: ["bravebird", "knockoff", "leafblade", "return", "roost", "swordsdance"],
532
+ randomBattleMoves: ["bravebird", "knockoff", "leafblade", "return", "swordsdance"],
533
533
  randomDoubleBattleMoves: ["bravebird", "knockoff", "leafblade", "protect", "return", "swordsdance"],
534
534
  tier: "(PU)",
535
535
  doublesTier: "(DUU)",
@@ -2486,7 +2486,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
2486
2486
  tier: "LC",
2487
2487
  },
2488
2488
  gastrodon: {
2489
- randomBattleMoves: ["clearsmog", "earthquake", "icebeam", "recover", "scald", "toxic"],
2489
+ randomBattleMoves: ["earthquake", "icebeam", "recover", "scald", "toxic"],
2490
2490
  randomDoubleBattleMoves: ["earthpower", "icywind", "muddywater", "protect", "recover", "scald"],
2491
2491
  tier: "PU",
2492
2492
  doublesTier: "DOU",
@@ -151,6 +151,10 @@ export const Moves: {[k: string]: ModdedMoveData} = {
151
151
  inherit: true,
152
152
  isNonstandard: null,
153
153
  },
154
+ dive: {
155
+ inherit: true,
156
+ flags: {contact: 1, charge: 1, protect: 1, mirror: 1, nonsky: 1},
157
+ },
154
158
  dizzypunch: {
155
159
  inherit: true,
156
160
  isNonstandard: null,
@@ -644,6 +648,10 @@ export const Moves: {[k: string]: ModdedMoveData} = {
644
648
  inherit: true,
645
649
  isNonstandard: null,
646
650
  },
651
+ pollenpuff: {
652
+ inherit: true,
653
+ flags: {bullet: 1, protect: 1, mirror: 1},
654
+ },
647
655
  powder: {
648
656
  inherit: true,
649
657
  isNonstandard: null,
package/data/moves.ts CHANGED
@@ -1399,7 +1399,7 @@ export const Moves: {[moveid: string]: MoveData} = {
1399
1399
  pp: 10,
1400
1400
  priority: 0,
1401
1401
  flags: {contact: 1, protect: 1, mirror: 1},
1402
- useSourceDefensiveAsOffensive: true,
1402
+ overrideOffensiveStat: 'def',
1403
1403
  secondary: null,
1404
1404
  target: "normal",
1405
1405
  type: "Fighting",
@@ -2282,7 +2282,7 @@ export const Moves: {[moveid: string]: MoveData} = {
2282
2282
  name: "Coaching",
2283
2283
  pp: 10,
2284
2284
  priority: 0,
2285
- flags: {bypasssub: 1},
2285
+ flags: {bypasssub: 1, allyanim: 1},
2286
2286
  secondary: null,
2287
2287
  boosts: {
2288
2288
  atk: 1,
@@ -3387,7 +3387,7 @@ export const Moves: {[moveid: string]: MoveData} = {
3387
3387
  name: "Dive",
3388
3388
  pp: 10,
3389
3389
  priority: 0,
3390
- flags: {contact: 1, charge: 1, protect: 1, mirror: 1, nonsky: 1},
3390
+ flags: {contact: 1, charge: 1, protect: 1, mirror: 1, nonsky: 1, allyanim: 1},
3391
3391
  onTryMove(attacker, defender, move) {
3392
3392
  if (attacker.removeVolatile(move.id)) {
3393
3393
  return;
@@ -5621,7 +5621,7 @@ export const Moves: {[moveid: string]: MoveData} = {
5621
5621
  pp: 15,
5622
5622
  priority: 0,
5623
5623
  flags: {contact: 1, protect: 1, mirror: 1},
5624
- useTargetOffensive: true,
5624
+ overrideOffensivePokemon: 'target',
5625
5625
  secondary: null,
5626
5626
  target: "normal",
5627
5627
  type: "Dark",
@@ -6509,9 +6509,9 @@ export const Moves: {[moveid: string]: MoveData} = {
6509
6509
  flags: {},
6510
6510
  isMax: "Melmetal",
6511
6511
  self: {
6512
- onHit(source) {
6512
+ onHit(source, target, effect) {
6513
6513
  for (const pokemon of source.foes()) {
6514
- if (!pokemon.volatiles['dynamax']) pokemon.addVolatile('torment');
6514
+ if (!pokemon.volatiles['dynamax']) pokemon.addVolatile('torment', source, effect);
6515
6515
  }
6516
6516
  },
6517
6517
  },
@@ -7148,7 +7148,7 @@ export const Moves: {[moveid: string]: MoveData} = {
7148
7148
  name: "Grassy Glide",
7149
7149
  pp: 20,
7150
7150
  priority: 0,
7151
- flags: {contact: 1, protect: 1, allyanim: 1},
7151
+ flags: {contact: 1, protect: 1},
7152
7152
  onModifyPriority(priority, source, target, move) {
7153
7153
  if (this.field.isTerrain('grassyterrain') && source.isGrounded()) {
7154
7154
  return priority + 1;
@@ -10423,7 +10423,7 @@ export const Moves: {[moveid: string]: MoveData} = {
10423
10423
  if (['gmaxoneblow', 'gmaxrapidflow'].includes(move.id)) return;
10424
10424
  /** moves blocked by Max Guard but not Protect */
10425
10425
  const overrideBypassProtect = [
10426
- 'block', 'flowershield', 'gearup', 'magneticflux', 'phantomforce', 'psychup', 'shadowforce', 'teatime', 'transform',
10426
+ 'block', 'flowershield', 'gearup', 'magneticflux', 'phantomforce', 'psychup', 'shadowforce', 'teatime', 'transform', 'whirlwind',
10427
10427
  ];
10428
10428
  const blockedByMaxGuard = (this.dex.moves.get(move.id).flags['protect'] ||
10429
10429
  move.isZ || move.isMax || overrideBypassProtect.includes(move.id));
@@ -12727,7 +12727,7 @@ export const Moves: {[moveid: string]: MoveData} = {
12727
12727
  name: "Pollen Puff",
12728
12728
  pp: 15,
12729
12729
  priority: 0,
12730
- flags: {bullet: 1, protect: 1, mirror: 1},
12730
+ flags: {bullet: 1, protect: 1, mirror: 1, allyanim: 1},
12731
12731
  onTryHit(target, source, move) {
12732
12732
  if (source.isAlly(target)) {
12733
12733
  move.basePower = 0;
@@ -13309,7 +13309,7 @@ export const Moves: {[moveid: string]: MoveData} = {
13309
13309
  accuracy: 100,
13310
13310
  basePower: 80,
13311
13311
  category: "Special",
13312
- defensiveCategory: "Physical",
13312
+ overrideDefensiveStat: 'def',
13313
13313
  name: "Psyshock",
13314
13314
  pp: 10,
13315
13315
  priority: 0,
@@ -13324,7 +13324,7 @@ export const Moves: {[moveid: string]: MoveData} = {
13324
13324
  accuracy: 100,
13325
13325
  basePower: 100,
13326
13326
  category: "Special",
13327
- defensiveCategory: "Physical",
13327
+ overrideDefensiveStat: 'def',
13328
13328
  name: "Psystrike",
13329
13329
  pp: 10,
13330
13330
  priority: 0,
@@ -14852,7 +14852,7 @@ export const Moves: {[moveid: string]: MoveData} = {
14852
14852
  accuracy: 100,
14853
14853
  basePower: 85,
14854
14854
  category: "Special",
14855
- defensiveCategory: "Physical",
14855
+ overrideDefensiveStat: 'def',
14856
14856
  name: "Secret Sword",
14857
14857
  pp: 10,
14858
14858
  priority: 0,
@@ -18312,11 +18312,12 @@ export const Moves: {[moveid: string]: MoveData} = {
18312
18312
  volatileStatus: 'torment',
18313
18313
  condition: {
18314
18314
  noCopy: true,
18315
- onStart(pokemon) {
18315
+ onStart(pokemon, source, effect) {
18316
18316
  if (pokemon.volatiles['dynamax']) {
18317
18317
  delete pokemon.volatiles['torment'];
18318
18318
  return false;
18319
18319
  }
18320
+ if (effect?.id === 'gmaxmeltdown') this.effectState.duration = 3;
18320
18321
  this.add('-start', pokemon, 'Torment');
18321
18322
  },
18322
18323
  onEnd(pokemon) {
@@ -19436,13 +19437,21 @@ export const Moves: {[moveid: string]: MoveData} = {
19436
19437
  }
19437
19438
  return 5;
19438
19439
  },
19440
+ onModifyMove(move, source, target) {
19441
+ // This code is for moves that use defensive stats as the attacking stat; see below for most of the implementation
19442
+ if (!move.overrideOffensiveStat) return;
19443
+ const statAndBoosts = move.overrideOffensiveStat;
19444
+ if (!['def', 'spd'].includes(statAndBoosts)) return;
19445
+ move.overrideOffensiveStat = statAndBoosts === 'def' ? 'spd' : 'def';
19446
+ this.hint(`${move.name} uses ${statAndBoosts === 'def' ? '' : 'Sp. '}Def boosts when Wonder Room is active.`);
19447
+ },
19439
19448
  onFieldStart(field, source) {
19440
19449
  this.add('-fieldstart', 'move: Wonder Room', '[of] ' + source);
19441
19450
  },
19442
19451
  onFieldRestart(target, source) {
19443
19452
  this.field.removePseudoWeather('wonderroom');
19444
19453
  },
19445
- // Swapping defenses implemented in sim/pokemon.js:Pokemon#calculateStat and Pokemon#getStat
19454
+ // Swapping defenses partially implemented in sim/pokemon.js:Pokemon#calculateStat and Pokemon#getStat
19446
19455
  onFieldResidualOrder: 27,
19447
19456
  onFieldResidualSubOrder: 5,
19448
19457
  onFieldEnd() {
package/data/rulesets.ts CHANGED
@@ -75,7 +75,7 @@ export const Rulesets: {[k: string]: FormatData} = {
75
75
  name: 'Standard NatDex',
76
76
  desc: "The standard ruleset for all National Dex tiers",
77
77
  ruleset: [
78
- 'Obtainable', '+Unobtainable', '+Past', 'Team Preview', 'Nickname Clause', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause',
78
+ 'Obtainable', '+Unobtainable', '+Past', 'Sketch Gen 8 Moves', 'Team Preview', 'Nickname Clause', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause',
79
79
  ],
80
80
  onValidateSet(set) {
81
81
  // These Pokemon are still unobtainable
@@ -280,9 +280,25 @@ export const Rulesets: {[k: string]: FormatData} = {
280
280
  }
281
281
  },
282
282
  },
283
- alolapokedex: {
283
+ oldalolapokedex: {
284
284
  effectType: 'ValidatorRule',
285
- name: 'Alola Pokedex',
285
+ name: 'Old Alola Pokedex',
286
+ desc: "Only allows Pokémon native to the Alola region (SUMO)",
287
+ banlist: ['Pikachu-Partner', 'Marowak-Alola-Totem', 'Ribombee-Totem', 'Araquanid-Totem', 'Lycanroc-Dusk', 'Necrozma-Dusk-Mane', 'Necrozma-Dawn-Wings'],
288
+ onValidateSet(set, format) {
289
+ const alolaDex = [
290
+ "Rowlet", "Dartrix", "Decidueye", "Litten", "Torracat", "Incineroar", "Popplio", "Brionne", "Primarina", "Pikipek", "Trumbeak", "Toucannon", "Yungoos", "Gumshoos", "Rattata-Alola", "Raticate-Alola", "Caterpie", "Metapod", "Butterfree", "Ledyba", "Ledian", "Spinarak", "Ariados", "Pichu", "Pikachu", "Raichu-Alola", "Grubbin", "Charjabug", "Vikavolt", "Bonsly", "Sudowoodo", "Happiny", "Chansey", "Blissey", "Munchlax", "Snorlax", "Slowpoke", "Slowbro", "Slowking", "Wingull", "Pelipper", "Abra", "Kadabra", "Alakazam", "Meowth-Alola", "Persian-Alola", "Magnemite", "Magneton", "Magnezone", "Grimer-Alola", "Muk-Alola", "Growlithe", "Arcanine", "Drowzee", "Hypno", "Makuhita", "Hariyama", "Smeargle", "Crabrawler", "Crabominable", "Gastly", "Haunter", "Gengar", "Drifloon", "Drifblim", "Misdreavus", "Mismagius", "Zubat", "Golbat", "Crobat", "Diglett-Alola", "Dugtrio-Alola", "Spearow", "Fearow", "Rufflet", "Braviary", "Vullaby", "Mandibuzz", "Mankey", "Primeape", "Delibird", "Oricorio", "Cutiefly", "Ribombee", "Petilil", "Lilligant", "Cottonee", "Whimsicott", "Psyduck", "Golduck", "Magikarp", "Gyarados", "Barboach", "Whiscash", "Machop", "Machoke", "Machamp", "Roggenrola", "Boldore", "Gigalith", "Carbink", "Sableye", "Rockruff", "Lycanroc", "Spinda", "Tentacool", "Tentacruel", "Finneon", "Lumineon", "Wishiwashi", "Luvdisc", "Corsola", "Mareanie", "Toxapex", "Shellder", "Cloyster", "Bagon", "Shelgon", "Salamence", "Lillipup", "Herdier", "Stoutland", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon", "Mudbray", "Mudsdale", "Igglybuff", "Jigglypuff", "Wigglytuff", "Tauros", "Miltank", "Surskit", "Masquerain", "Dewpider", "Araquanid", "Fomantis", "Lurantis", "Morelull", "Shiinotic", "Paras", "Parasect", "Poliwag", "Poliwhirl", "Poliwrath", "Politoed", "Goldeen", "Seaking", "Feebas", "Milotic", "Alomomola", "Fletchling", "Fletchinder", "Talonflame", "Salandit", "Salazzle", "Cubone", "Marowak-Alola", "Kangaskhan", "Magby", "Magmar", "Magmortar", "Stufful", "Bewear", "Bounsweet", "Steenee", "Tsareena", "Comfey", "Pinsir", "Oranguru", "Passimian", "Goomy", "Sliggoo", "Goodra", "Castform", "Wimpod", "Golisopod", "Staryu", "Starmie", "Sandygast", "Palossand", "Cranidos", "Rampardos", "Shieldon", "Bastiodon", "Archen", "Archeops", "Tirtouga", "Carracosta", "Phantump", "Trevenant", "Nosepass", "Probopass", "Pyukumuku", "Chinchou", "Lanturn", "Type: Null", "Silvally", "Zygarde", "Trubbish", "Garbodor", "Skarmory", "Ditto", "Cleffa", "Clefairy", "Clefable", "Minior", "Beldum", "Metang", "Metagross", "Porygon", "Porygon2", "Porygon-Z", "Pancham", "Pangoro", "Komala", "Torkoal", "Turtonator", "Togedemaru", "Elekid", "Electabuzz", "Electivire", "Geodude-Alola", "Graveler-Alola", "Golem-Alola", "Sandile", "Krokorok", "Krookodile", "Trapinch", "Vibrava", "Flygon", "Gible", "Gabite", "Garchomp", "Klefki", "Mimikyu", "Bruxish", "Drampa", "Absol", "Snorunt", "Glalie", "Froslass", "Sneasel", "Weavile", "Sandshrew-Alola", "Sandslash-Alola", "Vulpix-Alola", "Ninetales-Alola", "Vanillite", "Vanillish", "Vanilluxe", "Snubbull", "Granbull", "Shellos", "Gastrodon", "Relicanth", "Dhelmise", "Carvanha", "Sharpedo", "Wailmer", "Wailord", "Lapras", "Exeggcute", "Exeggutor-Alola", "Jangmo-o", "Hakamo-o", "Kommo-o", "Emolga", "Scyther", "Scizor", "Murkrow", "Honchkrow", "Riolu", "Lucario", "Dratini", "Dragonair", "Dragonite", "Aerodactyl", "Tapu Koko", "Tapu Lele", "Tapu Bulu", "Tapu Fini", "Cosmog", "Cosmoem", "Solgaleo", "Lunala", "Nihilego", "Buzzwole", "Pheromosa", "Xurkitree", "Celesteela", "Kartana", "Guzzlord", "Necrozma", "Magearna", "Marshadow",
291
+ ];
292
+ const species = this.dex.species.get(set.species || set.name);
293
+ if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name) &&
294
+ !this.ruleTable.has('+' + species.id)) {
295
+ return [`${species.baseSpecies} is not in the Old Alola Pokédex.`];
296
+ }
297
+ },
298
+ },
299
+ newalolapokedex: {
300
+ effectType: 'ValidatorRule',
301
+ name: 'New Alola Pokedex',
286
302
  desc: "Only allows Pokémon native to the Alola region (US/UM)",
287
303
  onValidateSet(set, format) {
288
304
  const alolaDex = [
@@ -291,7 +307,7 @@ export const Rulesets: {[k: string]: FormatData} = {
291
307
  const species = this.dex.species.get(set.species || set.name);
292
308
  if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name) &&
293
309
  !this.ruleTable.has('+' + species.id)) {
294
- return [`${species.baseSpecies} is not in the Alola Pokédex.`];
310
+ return [`${species.baseSpecies} is not in the New Alola Pokédex.`];
295
311
  }
296
312
  },
297
313
  },
@@ -550,6 +566,7 @@ export const Rulesets: {[k: string]: FormatData} = {
550
566
  this.add('rule', '2 Ability Clause: Limit two of each ability');
551
567
  },
552
568
  onValidateTeam(team) {
569
+ if (this.format.id === 'gen8multibility') return;
553
570
  const abilityTable = new Map<string, number>();
554
571
  const base: {[k: string]: string} = {
555
572
  airlock: 'cloudnine',
@@ -960,16 +977,14 @@ export const Rulesets: {[k: string]: FormatData} = {
960
977
  } else {
961
978
  typeTable = typeTable.filter(type => species.types.includes(type));
962
979
  }
963
- if (this.gen >= 7) {
964
- const item = this.dex.items.get(set.item);
965
- if (item.megaStone && species.baseSpecies === item.megaEvolves) {
966
- species = this.dex.species.get(item.megaStone);
967
- typeTable = typeTable.filter(type => species.types.includes(type));
968
- }
969
- if (item.id === "ultranecroziumz" && species.baseSpecies === "Necrozma") {
970
- species = this.dex.species.get("Necrozma-Ultra");
971
- typeTable = typeTable.filter(type => species.types.includes(type));
972
- }
980
+ const item = this.dex.items.get(set.item);
981
+ if (item.megaStone && species.baseSpecies === item.megaEvolves) {
982
+ species = this.dex.species.get(item.megaStone);
983
+ typeTable = typeTable.filter(type => species.types.includes(type));
984
+ }
985
+ if (item.id === "ultranecroziumz" && species.baseSpecies === "Necrozma") {
986
+ species = this.dex.species.get("Necrozma-Ultra");
987
+ typeTable = typeTable.filter(type => species.types.includes(type));
973
988
  }
974
989
  if (!typeTable.length) return [`Your team must share a type.`];
975
990
  }
@@ -1074,7 +1089,9 @@ export const Rulesets: {[k: string]: FormatData} = {
1074
1089
  if (!nonstandard && !move.isZ && !move.isMax && !this.ruleTable.isRestricted(`move:${move.id}`)) {
1075
1090
  const speciesTypes: string[] = [];
1076
1091
  const moveTypes: string[] = [];
1077
- for (let i = this.dex.gen; i >= species.gen && i >= move.gen; i--) {
1092
+ // BDSP can't import Pokemon from Home, so it shouldn't grant moves from archaic species types
1093
+ const minObtainableSpeciesGen = this.dex.currentMod === 'gen8bdsp' ? this.dex.gen : species.gen;
1094
+ for (let i = this.dex.gen; i >= minObtainableSpeciesGen && i >= move.gen; i--) {
1078
1095
  const dex = this.dex.forGen(i);
1079
1096
  moveTypes.push(dex.moves.get(move.name).type);
1080
1097
 
@@ -1188,6 +1205,12 @@ export const Rulesets: {[k: string]: FormatData} = {
1188
1205
  }
1189
1206
  },
1190
1207
  },
1208
+ 'sketchgen8moves': {
1209
+ effectType: 'ValidatorRule',
1210
+ name: 'Sketch Gen 8 Moves',
1211
+ desc: "Allows Pokémon who learn Sketch to learn any Gen 8 move (normally, Sketch is not usable in Gen 8).",
1212
+ // Implemented in sim/team-validator.ts
1213
+ },
1191
1214
  mimicglitch: {
1192
1215
  effectType: 'ValidatorRule',
1193
1216
  name: 'Mimic Glitch',
@@ -1371,7 +1394,7 @@ export const Rulesets: {[k: string]: FormatData} = {
1371
1394
  hasValue: 'positive-integer',
1372
1395
  // hardcoded in sim/side
1373
1396
  onValidateRule() {
1374
- if (!this.ruleTable.has('teampreview')) {
1397
+ if (!(this.ruleTable.has('teampreview') || this.ruleTable.has('teamtypepreview'))) {
1375
1398
  throw new Error(`The "Picked Team Size" rule${this.ruleTable.blame('pickedteamsize')} requires Team Preview.`);
1376
1399
  }
1377
1400
  },
@@ -1603,4 +1626,200 @@ export const Rulesets: {[k: string]: FormatData} = {
1603
1626
  this.lose(target.side);
1604
1627
  },
1605
1628
  },
1629
+ tiershiftmod: {
1630
+ effectType: "Rule",
1631
+ name: "Tier Shift Mod",
1632
+ desc: `Pok&eacute;mon below OU get their stats, excluding HP, boosted. UU/RUBL get +10, RU/NUBL get +20, NU/PUBL get +30, and PU or lower get +40.`,
1633
+ ruleset: ['Overflow Stat Mod'],
1634
+ onBegin() {
1635
+ this.add('rule', 'Tier Shift Mod: Pok\u00e9mon get stat buffs depending on their tier, excluding HP.');
1636
+ },
1637
+ onModifySpecies(species, target, source, effect) {
1638
+ if (!species.baseStats) return;
1639
+ const boosts: {[tier: string]: number} = {
1640
+ uu: 10,
1641
+ rubl: 10,
1642
+ ru: 20,
1643
+ nubl: 20,
1644
+ nu: 30,
1645
+ publ: 30,
1646
+ pu: 40,
1647
+ nfe: 40,
1648
+ lc: 40,
1649
+ };
1650
+ let tier: string = this.toID(species.tier);
1651
+ if (!(tier in boosts)) return;
1652
+ // Non-Pokemon bans in lower tiers
1653
+ if (target) {
1654
+ if (target.set.item === 'lightclay') return;
1655
+ if (['drizzle', 'drought', 'snowwarning'].includes(target.set.ability) && boosts[tier] > 20) tier = 'nubl';
1656
+ }
1657
+ const pokemon = this.dex.deepClone(species);
1658
+ pokemon.bst = pokemon.baseStats['hp'];
1659
+ const boost = boosts[tier];
1660
+ let statName: StatID;
1661
+ for (statName in pokemon.baseStats as StatsTable) {
1662
+ if (statName === 'hp') continue;
1663
+ pokemon.baseStats[statName] = this.clampIntRange(pokemon.baseStats[statName] + boost, 1, 255);
1664
+ pokemon.bst += pokemon.baseStats[statName];
1665
+ }
1666
+ return pokemon;
1667
+ },
1668
+ },
1669
+ crossevolutionmod: {
1670
+ effectType: "Rule",
1671
+ name: "Cross Evolution Mod",
1672
+ 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.",
1673
+ ruleset: ['Overflow Stat Mod'],
1674
+ onValidateTeam(team) {
1675
+ const names = new Set<ID>();
1676
+ for (const set of team) {
1677
+ const name = set.name;
1678
+ if (names.has(this.dex.toID(name))) {
1679
+ return [
1680
+ `Your Pok\u00e9mon must have different nicknames.`,
1681
+ `(You have more than one Pok\u00e9mon named '${name}')`,
1682
+ ];
1683
+ }
1684
+ names.add(this.dex.toID(name));
1685
+ }
1686
+ if (!names.size) {
1687
+ return [
1688
+ `${this.format.name} works using nicknames; your team has 0 nicknamed Pok\u00e9mon.`,
1689
+ `(If this was intentional, add a nickname to one Pok\u00e9mon that isn't the name of a Pok\u00e9mon species.)`,
1690
+ ];
1691
+ }
1692
+ },
1693
+ checkCanLearn(move, species, lsetData, set) {
1694
+ // @ts-ignore
1695
+ if (!set.sp?.exists || !set.crossSpecies?.exists) {
1696
+ return this.checkCanLearn(move, species, lsetData, set);
1697
+ }
1698
+ // @ts-ignore
1699
+ const problem = this.checkCanLearn(move, set.sp);
1700
+ if (!problem) return null;
1701
+ // @ts-ignore
1702
+ if (!set.crossMovesLeft) return problem;
1703
+ // @ts-ignore
1704
+ if (this.checkCanLearn(move, set.crossSpecies)) return problem;
1705
+ // @ts-ignore
1706
+ set.crossMovesLeft--;
1707
+ return null;
1708
+ },
1709
+ validateSet(set, teamHas) {
1710
+ const crossSpecies = this.dex.species.get(set.name);
1711
+ const onChangeSet = this.dex.formats.get('Pokemon').onChangeSet;
1712
+ let problems = onChangeSet?.call(this, set, this.format) || null;
1713
+ if (Array.isArray(problems) && problems.length) return problems;
1714
+ const crossNonstandard = !this.ruleTable.has('standardnatdex') && crossSpecies.isNonstandard === 'Past';
1715
+ const crossIsCap = !this.ruleTable.has('+pokemontag:cap') && crossSpecies.isNonstandard === 'CAP';
1716
+ if (!crossSpecies.exists || crossNonstandard || crossIsCap) return this.validateSet(set, teamHas);
1717
+ const species = this.dex.species.get(set.species);
1718
+ const check = this.checkSpecies(set, species, species, {});
1719
+ if (check) return [check];
1720
+ const nonstandard = !this.ruleTable.has('standardnatdex') && species.isNonstandard === 'Past';
1721
+ const isCap = !this.ruleTable.has('+pokemontag:cap') && species.isNonstandard === 'CAP';
1722
+ if (!species.exists || nonstandard || isCap || species === crossSpecies) return this.validateSet(set, teamHas);
1723
+ if (!species.nfe) return [`${species.name} cannot cross evolve because it doesn't evolve.`];
1724
+ const crossIsUnreleased = (crossSpecies.tier === "Unreleased" && crossSpecies.isNonstandard === "Unobtainable");
1725
+ if (crossSpecies.battleOnly || crossIsUnreleased || !crossSpecies.prevo) {
1726
+ return [`${species.name} cannot cross evolve into ${crossSpecies.name} because it isn't an evolution.`];
1727
+ }
1728
+ if (this.ruleTable.isRestrictedSpecies(crossSpecies)) {
1729
+ return [`${species.name} cannot cross evolve into ${crossSpecies.name} because it is banned.`];
1730
+ }
1731
+ const crossPrevoSpecies = this.dex.species.get(crossSpecies.prevo);
1732
+ if (!crossPrevoSpecies.prevo !== !species.prevo) {
1733
+ return [
1734
+ `${species.name} cannot cross evolve into ${crossSpecies.name} because they are not consecutive evolution stages.`,
1735
+ ];
1736
+ }
1737
+ const ability = this.dex.abilities.get(set.ability);
1738
+ if (!this.ruleTable.isRestricted(`ability:${ability.id}`) || Object.values(species.abilities).includes(ability.name)) {
1739
+ set.species = crossSpecies.name;
1740
+ }
1741
+
1742
+ // @ts-ignore
1743
+ set.sp = species;
1744
+ // @ts-ignore
1745
+ set.crossSpecies = crossSpecies;
1746
+ // @ts-ignore
1747
+ set.crossMovesLeft = 2;
1748
+ problems = this.validateSet(set, teamHas);
1749
+ set.name = crossSpecies.name;
1750
+ set.species = species.name;
1751
+ return problems;
1752
+ },
1753
+ onModifySpecies(species, target, source, effect) {
1754
+ if (!target) return; // chat
1755
+ if (effect && ['imposter', 'transform'].includes(effect.id)) return;
1756
+ if (target.set.name === target.set.species) return;
1757
+ const crossSpecies = this.dex.species.get(target.set.name);
1758
+ if (!crossSpecies.exists) return;
1759
+ if (species.battleOnly || !species.nfe) return;
1760
+ const crossIsUnreleased = (crossSpecies.tier === "Unreleased" && crossSpecies.isNonstandard === "Unobtainable");
1761
+ if (crossSpecies.battleOnly || crossIsUnreleased || !crossSpecies.prevo) return;
1762
+ const crossPrevoSpecies = this.dex.species.get(crossSpecies.prevo);
1763
+ if (!crossPrevoSpecies.prevo !== !species.prevo) return;
1764
+
1765
+ const mixedSpecies = this.dex.deepClone(species);
1766
+ mixedSpecies.baseSpecies = mixedSpecies.name = `${species.name}-${crossSpecies.name}`;
1767
+ mixedSpecies.weightkg =
1768
+ Math.max(0.1, +(species.weightkg + crossSpecies.weightkg - crossPrevoSpecies.weightkg)).toFixed(1);
1769
+ mixedSpecies.nfe = false;
1770
+ mixedSpecies.evos = [];
1771
+ mixedSpecies.eggGroups = crossSpecies.eggGroups;
1772
+ mixedSpecies.abilities = crossSpecies.abilities;
1773
+ mixedSpecies.bst = 0;
1774
+ let i: StatID;
1775
+ for (i in species.baseStats) {
1776
+ const statChange = crossSpecies.baseStats[i] - crossPrevoSpecies.baseStats[i];
1777
+ mixedSpecies.baseStats[i] = this.clampIntRange(species.baseStats[i] + statChange, 1, 255);
1778
+ mixedSpecies.bst += mixedSpecies.baseStats[i];
1779
+ }
1780
+ if (crossSpecies.types[0] !== crossPrevoSpecies.types[0]) mixedSpecies.types[0] = crossSpecies.types[0];
1781
+ if (crossSpecies.types[1] !== crossPrevoSpecies.types[1]) {
1782
+ mixedSpecies.types[1] = crossSpecies.types[1] || crossSpecies.types[0];
1783
+ }
1784
+ if (mixedSpecies.types[0] === mixedSpecies.types[1]) mixedSpecies.types = [mixedSpecies.types[0]];
1785
+
1786
+ return mixedSpecies;
1787
+ },
1788
+ onBegin() {
1789
+ for (const pokemon of this.getAllPokemon()) {
1790
+ pokemon.baseSpecies = pokemon.species;
1791
+ }
1792
+ },
1793
+ },
1794
+ revelationmonsmod: {
1795
+ effectType: "Rule",
1796
+ name: "Revelationmons Mod",
1797
+ desc: `The moves in the first slot(s) of a Pok&eacute;mon's set have their types changed to match the Pok&eacute;mon's type(s).`,
1798
+ onBegin() {
1799
+ this.add('rule', 'Revelationmons Mod: The first moveslots have their types changed to match the Pok&eacute;mon\'s types');
1800
+ },
1801
+ onValidateSet(set) {
1802
+ const species = this.dex.species.get(set.species);
1803
+ const slotIndex = species.types.length - 1;
1804
+ const problems = [];
1805
+ for (const [i, moveid] of set.moves.entries()) {
1806
+ const move = this.dex.moves.get(moveid);
1807
+ if (!this.ruleTable.isRestricted(`move:${move.id}`)) continue;
1808
+ if (i <= slotIndex) {
1809
+ problems.push(`${move.name} can't be in moveslot ${i + 1} because it's restricted from being in the first ${slotIndex + 1 > 1 ? `${slotIndex + 1} slots` : 'slot'}.`);
1810
+ }
1811
+ }
1812
+ return problems;
1813
+ },
1814
+ onModifyMove(move, pokemon, target) {
1815
+ const types = pokemon.getTypes(true);
1816
+ const noModifyType = [
1817
+ 'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball',
1818
+ ];
1819
+ if (noModifyType.includes(move.id)) return;
1820
+ for (const [i, type] of types.entries()) {
1821
+ if (pokemon.moveSlots[i] && move.id === pokemon.moveSlots[i].id) move.type = type;
1822
+ }
1823
+ },
1824
+ },
1606
1825
  };
package/data/tags.ts CHANGED
@@ -196,8 +196,8 @@ export const Tags: {[id: string]: TagData} = {
196
196
  name: "ND UUBL",
197
197
  speciesFilter: species => [
198
198
  'Aerodactyl-Mega', 'Azumarill', 'Blacephalon', 'Diancie-Mega', 'Gallade-Mega', 'Gardevoir-Mega', 'Gengar', 'Gyarados', 'Gyarados-Mega',
199
- 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios', 'Latios-Mega', 'Manaphy',
200
- 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega', 'Thundurus', 'Thundurus-Therian', 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
199
+ 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios', 'Latios-Mega', 'Manaphy', 'Mew',
200
+ 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega', 'Slowking-Galar', 'Thundurus', 'Thundurus-Therian', 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
201
201
  ].includes(species.name),
202
202
  },
203
203
 
@@ -209,7 +209,7 @@ export const Tags: {[id: string]: TagData} = {
209
209
  },
210
210
  dou: {
211
211
  name: "DOU",
212
- speciesFilter: species => species.doublesTier === 'DOU',
212
+ speciesFilter: species => species.doublesTier === 'DOU' || species.doublesTier === '(DOU)',
213
213
  },
214
214
  dbl: {
215
215
  name: "DBL",