@pkmn/sim 0.5.14 → 0.5.17

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 (127) hide show
  1. package/build/config/formats.js +167 -174
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/aliases.js +2 -2
  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 +3 -3
  8. package/build/data/formats-data.js.map +1 -1
  9. package/build/data/items.js +5 -5
  10. package/build/data/items.js.map +1 -1
  11. package/build/data/learnsets.js +7 -6
  12. package/build/data/learnsets.js.map +1 -1
  13. package/build/data/mods/gen1/formats-data.js +1 -1
  14. package/build/data/mods/gen1/formats-data.js.map +1 -1
  15. package/build/data/mods/gen1/moves.js +1 -4
  16. package/build/data/mods/gen1/moves.js.map +1 -1
  17. package/build/data/mods/gen1/scripts.js +1 -1
  18. package/build/data/mods/gen1/scripts.js.map +1 -1
  19. package/build/data/mods/gen2/items.js +28 -0
  20. package/build/data/mods/gen2/items.js.map +1 -1
  21. package/build/data/mods/gen2/moves.js +2 -5
  22. package/build/data/mods/gen2/moves.js.map +1 -1
  23. package/build/data/mods/gen3/items.js +28 -0
  24. package/build/data/mods/gen3/items.js.map +1 -1
  25. package/build/data/mods/gen3/moves.js +0 -10
  26. package/build/data/mods/gen3/moves.js.map +1 -1
  27. package/build/data/mods/gen3/scripts.js +2 -2
  28. package/build/data/mods/gen3/scripts.js.map +1 -1
  29. package/build/data/mods/gen4/abilities.js +20 -0
  30. package/build/data/mods/gen4/abilities.js.map +1 -1
  31. package/build/data/mods/gen4/items.js +28 -0
  32. package/build/data/mods/gen4/items.js.map +1 -1
  33. package/build/data/mods/gen4/moves.js +17 -10
  34. package/build/data/mods/gen4/moves.js.map +1 -1
  35. package/build/data/mods/gen4/rulesets.js +2 -1
  36. package/build/data/mods/gen4/rulesets.js.map +1 -1
  37. package/build/data/mods/gen5/items.js +0 -4
  38. package/build/data/mods/gen5/items.js.map +1 -1
  39. package/build/data/mods/gen5/rulesets.js +2 -1
  40. package/build/data/mods/gen5/rulesets.js.map +1 -1
  41. package/build/data/mods/gen6/items.js +24 -0
  42. package/build/data/mods/gen6/items.js.map +1 -1
  43. package/build/data/mods/gen7/abilities.js +31 -0
  44. package/build/data/mods/gen7/abilities.js.map +1 -1
  45. package/build/data/mods/gen7/formats-data.js +4 -4
  46. package/build/data/mods/gen7/formats-data.js.map +1 -1
  47. package/build/data/mods/gen7/items.js +12 -0
  48. package/build/data/mods/gen7/items.js.map +1 -1
  49. package/build/data/mods/gen7/moves.js +14 -0
  50. package/build/data/mods/gen7/moves.js.map +1 -1
  51. package/build/data/mods/gen7/rulesets.js +2 -1
  52. package/build/data/mods/gen7/rulesets.js.map +1 -1
  53. package/build/data/moves.js +84 -91
  54. package/build/data/moves.js.map +1 -1
  55. package/build/data/pokedex.js +2 -2
  56. package/build/data/pokedex.js.map +1 -1
  57. package/build/data/rulesets.js +74 -4
  58. package/build/data/rulesets.js.map +1 -1
  59. package/build/data/tags.js +3 -3
  60. package/build/data/tags.js.map +1 -1
  61. package/build/data/text/items.js +1 -0
  62. package/build/data/text/items.js.map +1 -1
  63. package/build/data/text/moves.js +13 -11
  64. package/build/data/text/moves.js.map +1 -1
  65. package/build/sim/battle-actions.js +12 -4
  66. package/build/sim/battle-actions.js.map +1 -1
  67. package/build/sim/battle-queue.d.ts +2 -2
  68. package/build/sim/battle-queue.js +8 -0
  69. package/build/sim/battle-queue.js.map +1 -1
  70. package/build/sim/battle.js +85 -7
  71. package/build/sim/battle.js.map +1 -1
  72. package/build/sim/dex-conditions.d.ts +3 -2
  73. package/build/sim/dex-conditions.js.map +1 -1
  74. package/build/sim/dex-moves.d.ts +2 -1
  75. package/build/sim/dex-moves.js.map +1 -1
  76. package/build/sim/exported-global-types.d.ts +1 -0
  77. package/build/sim/global-types.d.ts +1 -0
  78. package/build/sim/pokemon.js +18 -5
  79. package/build/sim/pokemon.js.map +1 -1
  80. package/build/sim/team-validator.js +16 -5
  81. package/build/sim/team-validator.js.map +1 -1
  82. package/build/sim/tools/exhaustive-runner.d.ts +8 -0
  83. package/build/sim/tools/exhaustive-runner.js +18 -7
  84. package/build/sim/tools/exhaustive-runner.js.map +1 -1
  85. package/config/formats.ts +172 -179
  86. package/data/aliases.ts +2 -2
  87. package/data/conditions.ts +4 -1
  88. package/data/formats-data.ts +3 -3
  89. package/data/items.ts +5 -5
  90. package/data/learnsets.ts +7 -6
  91. package/data/mods/gen1/formats-data.ts +1 -1
  92. package/data/mods/gen1/moves.ts +1 -4
  93. package/data/mods/gen1/scripts.ts +1 -1
  94. package/data/mods/gen2/items.ts +28 -0
  95. package/data/mods/gen2/moves.ts +2 -5
  96. package/data/mods/gen3/items.ts +28 -0
  97. package/data/mods/gen3/moves.ts +0 -10
  98. package/data/mods/gen3/scripts.ts +3 -3
  99. package/data/mods/gen4/abilities.ts +20 -0
  100. package/data/mods/gen4/items.ts +28 -0
  101. package/data/mods/gen4/moves.ts +16 -11
  102. package/data/mods/gen4/rulesets.ts +2 -1
  103. package/data/mods/gen5/items.ts +0 -4
  104. package/data/mods/gen5/rulesets.ts +2 -1
  105. package/data/mods/gen6/items.ts +24 -0
  106. package/data/mods/gen7/abilities.ts +31 -0
  107. package/data/mods/gen7/formats-data.ts +4 -4
  108. package/data/mods/gen7/items.ts +12 -0
  109. package/data/mods/gen7/moves.ts +14 -0
  110. package/data/mods/gen7/rulesets.ts +2 -1
  111. package/data/moves.ts +84 -81
  112. package/data/pokedex.ts +2 -2
  113. package/data/rulesets.ts +73 -6
  114. package/data/tags.ts +3 -3
  115. package/data/text/items.ts +2 -0
  116. package/data/text/moves.ts +14 -11
  117. package/package.json +2 -2
  118. package/sim/battle-actions.ts +16 -5
  119. package/sim/battle-queue.ts +10 -2
  120. package/sim/battle.ts +81 -7
  121. package/sim/dex-conditions.ts +7 -2
  122. package/sim/dex-moves.ts +2 -1
  123. package/sim/exported-global-types.ts +3 -0
  124. package/sim/global-types.ts +3 -0
  125. package/sim/pokemon.ts +19 -7
  126. package/sim/team-validator.ts +17 -5
  127. package/sim/tools/exhaustive-runner.ts +29 -11
@@ -66,6 +66,37 @@ export const Abilities: {[k: string]: ModdedAbilityData} = {
66
66
  inherit: true,
67
67
  onBoost() {},
68
68
  },
69
+ slowstart: {
70
+ inherit: true,
71
+ condition: {
72
+ duration: 5,
73
+ onResidualOrder: 28,
74
+ onResidualSubOrder: 2,
75
+ onStart(target) {
76
+ this.add('-start', target, 'ability: Slow Start');
77
+ },
78
+ onModifyAtkPriority: 5,
79
+ onModifyAtk(atk, pokemon, target, move) {
80
+ // This is because the game checks the move's category in data, rather than what it is currently, unlike e.g. Huge Power
81
+ if (this.dex.moves.get(move.id).category === 'Physical') {
82
+ return this.chainModify(0.5);
83
+ }
84
+ },
85
+ onModifySpAPriority: 5,
86
+ onModifySpA(spa, pokemon, target, move) {
87
+ // Ordinary Z-moves like Breakneck Blitz will halve the user's Special Attack as well
88
+ if (this.dex.moves.get(move.id).category === 'Physical') {
89
+ return this.chainModify(0.5);
90
+ }
91
+ },
92
+ onModifySpe(spe, pokemon) {
93
+ return this.chainModify(0.5);
94
+ },
95
+ onEnd(target) {
96
+ this.add('-end', target, 'Slow Start');
97
+ },
98
+ },
99
+ },
69
100
  soundproof: {
70
101
  inherit: true,
71
102
  onTryHit(target, source, move) {
@@ -3046,7 +3046,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
3046
3046
  doublesTier: "(DUU)",
3047
3047
  },
3048
3048
  sawk: {
3049
- randomBattleMoves: ["bulkup", "closecombat", "earthquake", "icepunch", "knockoff", "poisonjab"],
3049
+ randomBattleMoves: ["bulkup", "closecombat", "earthquake", "icepunch", "knockoff", "poisonjab", "stoneedge"],
3050
3050
  randomDoubleBattleMoves: ["closecombat", "icepunch", "knockoff", "protect", "rockslide"],
3051
3051
  tier: "PUBL",
3052
3052
  doublesTier: "(DUU)",
@@ -3362,7 +3362,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
3362
3362
  tier: "NFE",
3363
3363
  },
3364
3364
  chandelure: {
3365
- randomBattleMoves: ["calmmind", "energyball", "fireblast", "hiddenpowerground", "painsplit", "shadowball", "substitute", "trick"],
3365
+ randomBattleMoves: ["calmmind", "energyball", "fireblast", "hiddenpowerground", "shadowball", "substitute", "trick"],
3366
3366
  randomDoubleBattleMoves: ["energyball", "heatwave", "overheat", "protect", "shadowball", "trick"],
3367
3367
  tier: "UU",
3368
3368
  doublesTier: "DUU",
@@ -3485,7 +3485,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
3485
3485
  tier: "NFE",
3486
3486
  },
3487
3487
  hydreigon: {
3488
- randomBattleMoves: ["darkpulse", "dracometeor", "dragonpulse", "earthpower", "fireblast", "flashcannon", "roost", "superpower", "uturn"],
3488
+ randomBattleMoves: ["darkpulse", "dracometeor", "earthpower", "fireblast", "flashcannon", "roost", "superpower", "uturn"],
3489
3489
  randomDoubleBattleMoves: ["darkpulse", "dracometeor", "fireblast", "flashcannon", "protect", "tailwind", "uturn"],
3490
3490
  tier: "UU",
3491
3491
  doublesTier: "DUU",
@@ -3904,7 +3904,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
3904
3904
  tier: "NFE",
3905
3905
  },
3906
3906
  goodra: {
3907
- randomBattleMoves: ["dracometeor", "dragonpulse", "dragontail", "earthquake", "fireblast", "sludgebomb", "thunderbolt"],
3907
+ randomBattleMoves: ["dracometeor", "dragontail", "earthquake", "fireblast", "sludgebomb", "thunderbolt"],
3908
3908
  randomDoubleBattleMoves: ["dracometeor", "dragonpulse", "fireblast", "muddywater", "powerwhip", "protect", "thunderbolt"],
3909
3909
  tier: "RU",
3910
3910
  doublesTier: "(DUU)",
@@ -140,6 +140,10 @@ export const Items: {[k: string]: ModdedItemData} = {
140
140
  inherit: true,
141
141
  isNonstandard: null,
142
142
  },
143
+ dreamball: {
144
+ inherit: true,
145
+ isNonstandard: "Unobtainable",
146
+ },
143
147
  durinberry: {
144
148
  inherit: true,
145
149
  isNonstandard: "Unobtainable",
@@ -495,6 +499,10 @@ export const Items: {[k: string]: ModdedItemData} = {
495
499
  inherit: true,
496
500
  isNonstandard: null,
497
501
  },
502
+ safariball: {
503
+ inherit: true,
504
+ isNonstandard: "Unobtainable",
505
+ },
498
506
  sailfossil: {
499
507
  inherit: true,
500
508
  isNonstandard: null,
@@ -547,6 +555,10 @@ export const Items: {[k: string]: ModdedItemData} = {
547
555
  inherit: true,
548
556
  isNonstandard: null,
549
557
  },
558
+ sportball: {
559
+ inherit: true,
560
+ isNonstandard: "Unobtainable",
561
+ },
550
562
  steelgem: {
551
563
  inherit: true,
552
564
  isNonstandard: "Unobtainable",
@@ -259,6 +259,20 @@ export const Moves: {[k: string]: ModdedMoveData} = {
259
259
  return success;
260
260
  },
261
261
  },
262
+ fly: {
263
+ inherit: true,
264
+ onTryMove(attacker, defender, move) {
265
+ if (attacker.removeVolatile(move.id)) {
266
+ return;
267
+ }
268
+ this.add('-prepare', attacker, move.name);
269
+ if (!this.runEvent('ChargeMove', attacker, defender, move)) {
270
+ return;
271
+ }
272
+ attacker.addVolatile('twoturnmove', defender);
273
+ return null;
274
+ },
275
+ },
262
276
  foresight: {
263
277
  inherit: true,
264
278
  isNonstandard: null,
@@ -36,7 +36,8 @@ export const Rulesets: {[k: string]: ModdedFormatData} = {
36
36
  this.add('clearpoke');
37
37
  for (const pokemon of this.getAllPokemon()) {
38
38
  const details = pokemon.details.replace(', shiny', '')
39
- .replace(/(Arceus|Gourgeist|Genesect|Pumpkaboo|Silvally|Zacian|Zamazenta|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*');
39
+ .replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*')
40
+ .replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*'); // Hacked-in Crowned formes will be revealed
40
41
  this.add('poke', pokemon.side.id, details, pokemon.item ? 'item' : '');
41
42
  }
42
43
  this.makeRequest('teampreview');
package/data/moves.ts CHANGED
@@ -1035,7 +1035,7 @@ export const Moves: {[moveid: string]: MoveData} = {
1035
1035
  pp: 15,
1036
1036
  priority: -3,
1037
1037
  flags: {bullet: 1, protect: 1},
1038
- beforeTurnCallback(pokemon) {
1038
+ priorityChargeCallback(pokemon) {
1039
1039
  pokemon.addVolatile('beakblast');
1040
1040
  },
1041
1041
  condition: {
@@ -2640,7 +2640,7 @@ export const Moves: {[moveid: string]: MoveData} = {
2640
2640
  beforeTurnCallback(pokemon) {
2641
2641
  pokemon.addVolatile('counter');
2642
2642
  },
2643
- onTryHit(target, source, move) {
2643
+ onTry(source) {
2644
2644
  if (!source.volatiles['counter']) return false;
2645
2645
  if (source.volatiles['counter'].slot === null) return false;
2646
2646
  },
@@ -2689,53 +2689,50 @@ export const Moves: {[moveid: string]: MoveData} = {
2689
2689
  const offset = this.random(3) + 1;
2690
2690
  // the list of all sides in counterclockwise order
2691
2691
  const sides = [this.sides[0], this.sides[2]!, this.sides[1], this.sides[3]!];
2692
- for (const id of sideConditions) {
2693
- const effectName = this.dex.conditions.get(id).name;
2694
- const rotatedSides = [];
2695
- let someCondition = false;
2696
- for (let i = 0; i < 4; i++) {
2697
- const sourceSide = sides[i];
2698
- const targetSide = sides[(i + offset) % 4]; // the next side in rotation
2699
- rotatedSides.push(targetSide.sideConditions[id]);
2700
- if (sourceSide.sideConditions[id]) {
2701
- this.add('-sideend', sourceSide, effectName, '[silent]');
2702
- someCondition = true;
2703
- }
2692
+ const temp: {[k: number]: typeof source.side.sideConditions} = {0: {}, 1: {}, 2: {}, 3: {}};
2693
+ for (const side of sides) {
2694
+ for (const id in side.sideConditions) {
2695
+ if (!sideConditions.includes(id)) continue;
2696
+ temp[side.n][id] = side.sideConditions[id];
2697
+ delete side.sideConditions[id];
2698
+ const effectName = this.dex.conditions.get(id).name;
2699
+ this.add('-sideend', side, effectName, '[silent]');
2700
+ success = true;
2704
2701
  }
2705
- if (!someCondition) continue;
2706
- [
2707
- sides[0].sideConditions[id], sides[1].sideConditions[id],
2708
- sides[2]!.sideConditions[id], sides[3]!.sideConditions[id],
2709
- ] = [...rotatedSides];
2710
- for (const side of sides) {
2711
- if (side.sideConditions[id]) {
2712
- let layers = side.sideConditions[id].layers || 1;
2713
- for (; layers > 0; layers--) this.add('-sidestart', side, effectName, '[silent]');
2714
- } else {
2715
- delete side.sideConditions[id];
2716
- }
2702
+ }
2703
+ for (let i = 0; i < 4; i++) {
2704
+ const sourceSideConditions = temp[sides[i].n];
2705
+ const targetSide = sides[(i + offset) % 4]; // the next side in rotation
2706
+ for (const id in sourceSideConditions) {
2707
+ targetSide.sideConditions[id] = sourceSideConditions[id];
2708
+ const effectName = this.dex.conditions.get(id).name;
2709
+ let layers = sourceSideConditions[id].layers || 1;
2710
+ for (; layers > 0; layers--) this.add('-sidestart', targetSide, effectName, '[silent]');
2717
2711
  }
2718
- success = true;
2719
2712
  }
2720
2713
  } else {
2721
- const sourceSide = source.side;
2722
- const targetSide = source.side.foe;
2723
- for (const id of sideConditions) {
2724
- if (sourceSide.sideConditions[id] && targetSide.sideConditions[id]) {
2725
- [sourceSide.sideConditions[id], targetSide.sideConditions[id]] = [
2726
- targetSide.sideConditions[id], sourceSide.sideConditions[id],
2727
- ];
2728
- } else if (sourceSide.sideConditions[id] && !targetSide.sideConditions[id]) {
2729
- targetSide.sideConditions[id] = sourceSide.sideConditions[id];
2730
- delete sourceSide.sideConditions[id];
2731
- } else if (targetSide.sideConditions[id] && !sourceSide.sideConditions[id]) {
2732
- sourceSide.sideConditions[id] = targetSide.sideConditions[id];
2733
- delete targetSide.sideConditions[id];
2734
- } else {
2735
- continue;
2736
- }
2714
+ const sourceSideConditions = source.side.sideConditions;
2715
+ const targetSideConditions = source.side.foe.sideConditions;
2716
+ const sourceTemp: typeof sourceSideConditions = {};
2717
+ const targetTemp: typeof targetSideConditions = {};
2718
+ for (const id in sourceSideConditions) {
2719
+ if (!sideConditions.includes(id)) continue;
2720
+ sourceTemp[id] = sourceSideConditions[id];
2721
+ delete sourceSideConditions[id];
2722
+ success = true;
2723
+ }
2724
+ for (const id in targetSideConditions) {
2725
+ if (!sideConditions.includes(id)) continue;
2726
+ targetTemp[id] = targetSideConditions[id];
2727
+ delete targetSideConditions[id];
2737
2728
  success = true;
2738
2729
  }
2730
+ for (const id in sourceTemp) {
2731
+ targetSideConditions[id] = sourceTemp[id];
2732
+ }
2733
+ for (const id in targetTemp) {
2734
+ sourceSideConditions[id] = targetTemp[id];
2735
+ }
2739
2736
  this.add('-swapsideconditions');
2740
2737
  }
2741
2738
  if (!success) return false;
@@ -3092,6 +3089,7 @@ export const Moves: {[moveid: string]: MoveData} = {
3092
3089
  volatileStatus: 'defensecurl',
3093
3090
  condition: {
3094
3091
  noCopy: true,
3092
+ onRestart: () => null,
3095
3093
  },
3096
3094
  secondary: null,
3097
3095
  target: "self",
@@ -4798,7 +4796,6 @@ export const Moves: {[moveid: string]: MoveData} = {
4798
4796
  pp: 5,
4799
4797
  priority: 0,
4800
4798
  flags: {protect: 1},
4801
- selfdestruct: "ifHit",
4802
4799
  secondary: null,
4803
4800
  target: "normal",
4804
4801
  type: "Fighting",
@@ -5390,6 +5387,17 @@ export const Moves: {[moveid: string]: MoveData} = {
5390
5387
  if (!this.runEvent('ChargeMove', attacker, defender, move)) {
5391
5388
  return;
5392
5389
  }
5390
+
5391
+ // In SwSh, Fly's animation leaks the initial target through a camera focus
5392
+ // The animation leak target itself isn't "accurate"; the target it reveals is as if Fly weren't a charge movee
5393
+ // (Fly, like all other charge moves, will actually target slots on its charging turn, relevant for things like Follow Me)
5394
+ // We use a generic single-target move to represent this
5395
+ if (this.gameType === 'doubles' || this.gameType === 'multi') {
5396
+ const animatedTarget = attacker.getMoveTargets(this.dex.getActiveMove('aerialace'), defender).targets[0];
5397
+ if (animatedTarget) {
5398
+ this.hint(`${move.name}'s animation targeted ${animatedTarget.name}`);
5399
+ }
5400
+ }
5393
5401
  attacker.addVolatile('twoturnmove', defender);
5394
5402
  return null;
5395
5403
  },
@@ -5488,11 +5496,11 @@ export const Moves: {[moveid: string]: MoveData} = {
5488
5496
  pp: 20,
5489
5497
  priority: -3,
5490
5498
  flags: {contact: 1, protect: 1, punch: 1},
5491
- beforeTurnCallback(pokemon) {
5499
+ priorityChargeCallback(pokemon) {
5492
5500
  pokemon.addVolatile('focuspunch');
5493
5501
  },
5494
5502
  beforeMoveCallback(pokemon) {
5495
- if (pokemon.volatiles['focuspunch'] && pokemon.volatiles['focuspunch'].lostFocus) {
5503
+ if (pokemon.volatiles['focuspunch']?.lostFocus) {
5496
5504
  this.add('cant', pokemon, 'Focus Punch', 'Focus Punch');
5497
5505
  return true;
5498
5506
  }
@@ -5504,7 +5512,7 @@ export const Moves: {[moveid: string]: MoveData} = {
5504
5512
  },
5505
5513
  onHit(pokemon, source, move) {
5506
5514
  if (move.category !== 'Status') {
5507
- pokemon.volatiles['focuspunch'].lostFocus = true;
5515
+ this.effectState.lostFocus = true;
5508
5516
  }
5509
5517
  },
5510
5518
  onTryAddVolatile(status, pokemon) {
@@ -6708,7 +6716,7 @@ export const Moves: {[moveid: string]: MoveData} = {
6708
6716
  onSideStart(side) {
6709
6717
  this.add('-sidestart', side, 'move: G-Max Steelsurge');
6710
6718
  },
6711
- onSwitchIn(pokemon) {
6719
+ onEntryHazard(pokemon) {
6712
6720
  if (pokemon.hasItem('heavydutyboots')) return;
6713
6721
  // Ice Face and Disguise correctly get typed damage from Stealth Rock
6714
6722
  // because Stealth Rock bypasses Substitute.
@@ -7808,10 +7816,10 @@ export const Moves: {[moveid: string]: MoveData} = {
7808
7816
  priority: 0,
7809
7817
  flags: {snatch: 1, heal: 1},
7810
7818
  onHit(target, source, move) {
7811
- if (!this.canSwitch(target.side)) {
7819
+ if (!this.canSwitch(source.side)) {
7812
7820
  delete move.selfdestruct;
7813
7821
  this.attrLastMove('[still]');
7814
- this.add('-fail', target);
7822
+ this.add('-fail', source);
7815
7823
  return this.NOT_FAIL;
7816
7824
  }
7817
7825
  },
@@ -9904,10 +9912,10 @@ export const Moves: {[moveid: string]: MoveData} = {
9904
9912
  priority: 0,
9905
9913
  flags: {snatch: 1, heal: 1, dance: 1},
9906
9914
  onHit(target, source, move) {
9907
- if (!this.canSwitch(target.side)) {
9915
+ if (!this.canSwitch(source.side)) {
9908
9916
  delete move.selfdestruct;
9909
9917
  this.attrLastMove('[still]');
9910
- this.add('-fail', target);
9918
+ this.add('-fail', source);
9911
9919
  return this.NOT_FAIL;
9912
9920
  }
9913
9921
  },
@@ -10925,7 +10933,7 @@ export const Moves: {[moveid: string]: MoveData} = {
10925
10933
  pp: 10,
10926
10934
  priority: 0,
10927
10935
  flags: {protect: 1, mirror: 1},
10928
- onTryHit(target, source, move) {
10936
+ onTry(source) {
10929
10937
  const lastDamagedBy = source.getLastDamagedBy(true);
10930
10938
  if (lastDamagedBy === undefined || !lastDamagedBy.thisTurn) return false;
10931
10939
  },
@@ -11051,26 +11059,21 @@ export const Moves: {[moveid: string]: MoveData} = {
11051
11059
  priority: 0,
11052
11060
  flags: {},
11053
11061
  noMetronome: [
11054
- "After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
11062
+ "After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Dragon Hammer", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
11055
11063
  ],
11056
11064
  onHit(target, source, effect) {
11057
- const moves: MoveData[] = [];
11058
- for (const id in Moves) {
11059
- const move = Moves[id];
11060
- if (move.realMove) continue;
11061
- if (move.isZ || move.isMax || move.isNonstandard) continue;
11062
- if (effect.noMetronome!.includes(move.name)) continue;
11063
- if (this.dex.moves.get(id).gen > this.gen) continue;
11064
- moves.push(move);
11065
- }
11065
+ const moves = this.dex.moves.all().filter(move => (
11066
+ (![2, 4].includes(this.gen) || !source.moves.includes(move.id)) &&
11067
+ !move.realMove && !move.isZ && !move.isMax &&
11068
+ (!move.isNonstandard || move.isNonstandard === 'Unobtainable') &&
11069
+ !effect.noMetronome!.includes(move.name)
11070
+ ));
11066
11071
  let randomMove = '';
11067
11072
  if (moves.length) {
11068
- moves.sort((a, b) => a.num! - b.num!);
11069
- randomMove = this.sample(moves).name;
11070
- }
11071
- if (!randomMove) {
11072
- return false;
11073
+ moves.sort((a, b) => a.num - b.num);
11074
+ randomMove = this.sample(moves).id;
11073
11075
  }
11076
+ if (!randomMove) return false;
11074
11077
  this.actions.useMove(randomMove, target);
11075
11078
  },
11076
11079
  secondary: null,
@@ -11187,6 +11190,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11187
11190
  volatileStatus: 'minimize',
11188
11191
  condition: {
11189
11192
  noCopy: true,
11193
+ onRestart: () => null,
11190
11194
  onSourceModifyDamage(damage, source, target, move) {
11191
11195
  const boostedMoves = [
11192
11196
  'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault',
@@ -11264,7 +11268,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11264
11268
  beforeTurnCallback(pokemon) {
11265
11269
  pokemon.addVolatile('mirrorcoat');
11266
11270
  },
11267
- onTryHit(target, source, move) {
11271
+ onTry(source) {
11268
11272
  if (!source.volatiles['mirrorcoat']) return false;
11269
11273
  if (source.volatiles['mirrorcoat'].slot === null) return false;
11270
11274
  },
@@ -13478,7 +13482,7 @@ export const Moves: {[moveid: string]: MoveData} = {
13478
13482
  let alreadyAdded = false;
13479
13483
  pokemon.removeVolatile('destinybond');
13480
13484
  for (const source of this.effectState.sources) {
13481
- if (!this.queue.cancelMove(source) || !source.hp) continue;
13485
+ if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp) continue;
13482
13486
  if (!alreadyAdded) {
13483
13487
  this.add('-activate', pokemon, 'move: Pursuit');
13484
13488
  alreadyAdded = true;
@@ -14000,7 +14004,8 @@ export const Moves: {[moveid: string]: MoveData} = {
14000
14004
  }
14001
14005
  },
14002
14006
  onHit(target, source, move) {
14003
- if (!target.setStatus('slp', source, move)) return false;
14007
+ const result = target.setStatus('slp', source, move);
14008
+ if (!result) return result;
14004
14009
  target.statusState.time = 3;
14005
14010
  target.statusState.startTime = 3;
14006
14011
  this.heal(target.maxhp); // Aesthetic only as the healing happens after you fall asleep in-game
@@ -15210,7 +15215,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15210
15215
  pp: 5,
15211
15216
  priority: -3,
15212
15217
  flags: {protect: 1},
15213
- beforeTurnCallback(pokemon) {
15218
+ priorityChargeCallback(pokemon) {
15214
15219
  pokemon.addVolatile('shelltrap');
15215
15220
  },
15216
15221
  onTryMove(pokemon) {
@@ -15227,7 +15232,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15227
15232
  },
15228
15233
  onHit(pokemon, source, move) {
15229
15234
  if (!pokemon.isAlly(source) && move.category === 'Physical') {
15230
- pokemon.volatiles['shelltrap'].gotHit = true;
15235
+ this.effectState.gotHit = true;
15231
15236
  const action = this.queue.willMove(pokemon);
15232
15237
  if (action) {
15233
15238
  this.queue.prioritizeAction(action);
@@ -16398,9 +16403,8 @@ export const Moves: {[moveid: string]: MoveData} = {
16398
16403
  this.add('-sidestart', side, 'Spikes');
16399
16404
  this.effectState.layers++;
16400
16405
  },
16401
- onSwitchIn(pokemon) {
16402
- if (!pokemon.isGrounded()) return;
16403
- if (pokemon.hasItem('heavydutyboots')) return;
16406
+ onEntryHazard(pokemon) {
16407
+ if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
16404
16408
  const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4
16405
16409
  this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
16406
16410
  },
@@ -16683,7 +16687,7 @@ export const Moves: {[moveid: string]: MoveData} = {
16683
16687
  onSideStart(side) {
16684
16688
  this.add('-sidestart', side, 'move: Stealth Rock');
16685
16689
  },
16686
- onSwitchIn(pokemon) {
16690
+ onEntryHazard(pokemon) {
16687
16691
  if (pokemon.hasItem('heavydutyboots')) return;
16688
16692
  const typeMod = this.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6);
16689
16693
  this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8);
@@ -16807,9 +16811,8 @@ export const Moves: {[moveid: string]: MoveData} = {
16807
16811
  onSideStart(side) {
16808
16812
  this.add('-sidestart', side, 'move: Sticky Web');
16809
16813
  },
16810
- onSwitchIn(pokemon) {
16811
- if (!pokemon.isGrounded()) return;
16812
- if (pokemon.hasItem('heavydutyboots')) return;
16814
+ onEntryHazard(pokemon) {
16815
+ if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
16813
16816
  this.add('-activate', pokemon, 'move: Sticky Web');
16814
16817
  this.boost({spe: -1}, pokemon, this.effectState.source, this.dex.getActiveMove('stickyweb'));
16815
16818
  },
@@ -18413,7 +18416,7 @@ export const Moves: {[moveid: string]: MoveData} = {
18413
18416
  this.add('-sidestart', side, 'move: Toxic Spikes');
18414
18417
  this.effectState.layers++;
18415
18418
  },
18416
- onSwitchIn(pokemon) {
18419
+ onEntryHazard(pokemon) {
18417
18420
  if (!pokemon.isGrounded()) return;
18418
18421
  if (pokemon.hasType('Poison')) {
18419
18422
  this.add('-sideend', pokemon.side, 'move: Toxic Spikes', '[of] ' + pokemon);
package/data/pokedex.ts CHANGED
@@ -16034,7 +16034,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
16034
16034
  color: "Blue",
16035
16035
  eggGroups: ["Undiscovered"],
16036
16036
  requiredItem: "Rusted Sword",
16037
- changesFrom: "Zacian",
16037
+ battleOnly: "Zacian",
16038
16038
  cannotDynamax: true,
16039
16039
  },
16040
16040
  zamazenta: {
@@ -16068,7 +16068,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
16068
16068
  color: "Red",
16069
16069
  eggGroups: ["Undiscovered"],
16070
16070
  requiredItem: "Rusted Shield",
16071
- changesFrom: "Zamazenta",
16071
+ battleOnly: "Zamazenta",
16072
16072
  cannotDynamax: true,
16073
16073
  },
16074
16074
  eternatus: {
package/data/rulesets.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Note: These are the rules that formats use
2
2
 
3
3
  import {Utils} from "../lib";
4
+ import {Pokemon} from "../sim/pokemon";
4
5
 
5
6
  // The list of formats is stored in config/formats.js
6
7
  export const Rulesets: {[k: string]: FormatData} = {
@@ -445,7 +446,8 @@ export const Rulesets: {[k: string]: FormatData} = {
445
446
  this.add('clearpoke');
446
447
  for (const pokemon of this.getAllPokemon()) {
447
448
  const details = pokemon.details.replace(', shiny', '')
448
- .replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Zacian|Zamazenta|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*');
449
+ .replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*')
450
+ .replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*'); // Hacked-in Crowned formes will be revealed
449
451
  this.add('poke', pokemon.side.id, details, '');
450
452
  }
451
453
  this.makeRequest('teampreview');
@@ -1145,11 +1147,7 @@ export const Rulesets: {[k: string]: FormatData} = {
1145
1147
  if (prevo.evos.includes(formeName)) continue;
1146
1148
  }
1147
1149
  const forme = dex.species.get(formeName);
1148
- if (
1149
- forme.changesFrom === originalForme.name && !forme.battleOnly &&
1150
- // Temporary workaround
1151
- forme.forme !== 'Crowned'
1152
- ) {
1150
+ if (forme.changesFrom === originalForme.name && !forme.battleOnly) {
1153
1151
  speciesTypes.push(...forme.types);
1154
1152
  }
1155
1153
  }
@@ -1604,6 +1602,75 @@ export const Rulesets: {[k: string]: FormatData} = {
1604
1602
  pokemon.trapped = true;
1605
1603
  },
1606
1604
  },
1605
+ crazyhouserule: {
1606
+ effectType: 'Rule',
1607
+ name: 'Crazyhouse Rule',
1608
+ desc: "Pok\u00e9mon you KO are added to your team and removed from the opponent's, and vice versa.",
1609
+ onValidateRule(value) {
1610
+ if (this.format.gameType === 'doubles' || this.format.gameType === 'triples') {
1611
+ throw new Error(`Crazyhouse Rule currently does not support ${this.format.gameType} battles.`);
1612
+ }
1613
+ const ruleTable = this.ruleTable;
1614
+ const maxTeamSize = ruleTable.pickedTeamSize || ruleTable.maxTeamSize;
1615
+ const numPlayers = (this.format.gameType === 'freeforall' || this.format.gameType === 'multi') ? 4 : 2;
1616
+ const potentialMaxTeamSize = maxTeamSize * numPlayers;
1617
+ if (potentialMaxTeamSize > 24) {
1618
+ throw new Error(`Crazyhouse Rule cannot be added because a team can potentially have ${potentialMaxTeamSize} Pokemon on one team, which is more than the server limit of 24.`);
1619
+ }
1620
+ },
1621
+ // In order to prevent a case of the clones, housekeeping is needed.
1622
+ // This is especially needed to make sure one side doesn't end up with too many Pokemon.
1623
+ onBeforeSwitchIn(pokemon) {
1624
+ if (this.turn < 1 || !pokemon.side.faintedThisTurn) return;
1625
+ pokemon.side.pokemon = pokemon.side.pokemon.filter(x => !(x.fainted && !x.m.outofplay));
1626
+ for (let i = 0; i < pokemon.side.pokemon.length && i < 24; i++) {
1627
+ pokemon.side.pokemon[i].position = i;
1628
+ }
1629
+ },
1630
+ onFaint(target, source, effect) {
1631
+ if (!target.m.numSwaps) {
1632
+ target.m.numSwaps = 0;
1633
+ }
1634
+ target.m.numSwaps++;
1635
+ if (effect && effect.effectType === 'Move' && source.side.pokemon.length < 24 &&
1636
+ source.side !== target.side && target.m.numSwaps < 4) {
1637
+ const hpCost = this.clampIntRange(Math.floor((target.baseMaxhp * target.m.numSwaps) / 4), 1);
1638
+ // Just in case(tm) and for Shedinja
1639
+ if (hpCost === target.baseMaxhp) {
1640
+ target.m.outofplay = true;
1641
+ return;
1642
+ }
1643
+ source.side.pokemonLeft++;
1644
+ source.side.pokemon.length++;
1645
+
1646
+ // A new Pokemon is created and stuff gets aside akin to a deep clone.
1647
+ // This is because deepClone crashes when side is called recursively.
1648
+ // Until a refactor is made to prevent it, this is the best option to prevent crashes.
1649
+ const newPoke = new Pokemon(target.set, source.side);
1650
+ const newPos = source.side.pokemon.length - 1;
1651
+
1652
+ const doNotCarryOver = [
1653
+ 'fullname', 'side', 'fainted', 'status', 'hp', 'illusion',
1654
+ 'transformed', 'position', 'isActive', 'faintQueued',
1655
+ 'subFainted', 'getHealth', 'getDetails', 'moveSlots', 'ability',
1656
+ ];
1657
+ for (const [key, value] of Object.entries(target)) {
1658
+ if (doNotCarryOver.includes(key)) continue;
1659
+ // @ts-ignore
1660
+ newPoke[key] = value;
1661
+ }
1662
+ newPoke.maxhp = newPoke.baseMaxhp; // for dynamax
1663
+ newPoke.hp = newPoke.baseMaxhp - hpCost;
1664
+ newPoke.clearVolatile();
1665
+ newPoke.position = newPos;
1666
+ source.side.pokemon[newPos] = newPoke;
1667
+ this.add('poke', source.side.pokemon[newPos].side.id, source.side.pokemon[newPos].details, '');
1668
+ this.add('-message', `${target.name} was captured by ${newPoke.side.name}!`);
1669
+ } else {
1670
+ target.m.outofplay = true;
1671
+ }
1672
+ },
1673
+ },
1607
1674
  chimera1v1rule: {
1608
1675
  effectType: 'Rule',
1609
1676
  name: 'Chimera 1v1 Rule',
package/data/tags.ts CHANGED
@@ -198,9 +198,9 @@ export const Tags: {[id: string]: TagData} = {
198
198
  name: "ND UUBL",
199
199
  speciesFilter: species => [
200
200
  'Aerodactyl-Mega', 'Alakazam', 'Blacephalon', 'Blaziken', 'Diancie-Mega', 'Gallade-Mega', 'Gardevoir-Mega', 'Gengar', 'Gyarados',
201
- 'Gyarados-Mega', 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios', 'Latios-Mega',
202
- 'Manaphy', 'Medicham-Mega', 'Mew', 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega', 'Slowking-Galar', 'Thundurus', 'Thundurus-Therian',
203
- 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
201
+ 'Gyarados-Mega', 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios',
202
+ 'Latios-Mega', 'Manaphy', 'Medicham-Mega', 'Melmetal', 'Mew', 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega', 'Slowking-Galar',
203
+ 'Thundurus', 'Thundurus-Therian', 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
204
204
  ].includes(species.name),
205
205
  },
206
206
 
@@ -429,6 +429,8 @@ export const ItemsText: {[k: string]: ItemText} = {
429
429
  ejectpack: {
430
430
  name: "Eject Pack",
431
431
  desc: "If the holder's stat stages are lowered, it switches to a chosen ally. Single use.",
432
+
433
+ end: " [POKEMON] is switched out by the Eject Pack!",
432
434
  },
433
435
  electirizer: {
434
436
  name: "Electirizer",