@pkmn/sim 0.5.12 → 0.5.15

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 (109) hide show
  1. package/build/config/formats.js +163 -149
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/abilities.js +2 -2
  4. package/build/data/abilities.js.map +1 -1
  5. package/build/data/aliases.js +2 -2
  6. package/build/data/aliases.js.map +1 -1
  7. package/build/data/conditions.js +4 -1
  8. package/build/data/conditions.js.map +1 -1
  9. package/build/data/formats-data.js +2 -2
  10. package/build/data/formats-data.js.map +1 -1
  11. package/build/data/items.js +4 -2
  12. package/build/data/items.js.map +1 -1
  13. package/build/data/learnsets.js +5 -4
  14. package/build/data/learnsets.js.map +1 -1
  15. package/build/data/mods/gen1/formats-data.js +38 -36
  16. package/build/data/mods/gen1/formats-data.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 -2
  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/gen4/abilities.js +20 -0
  26. package/build/data/mods/gen4/abilities.js.map +1 -1
  27. package/build/data/mods/gen4/items.js +28 -0
  28. package/build/data/mods/gen4/items.js.map +1 -1
  29. package/build/data/mods/gen4/moves.js +14 -6
  30. package/build/data/mods/gen4/moves.js.map +1 -1
  31. package/build/data/mods/gen5/items.js +0 -4
  32. package/build/data/mods/gen5/items.js.map +1 -1
  33. package/build/data/mods/gen6/formats-data.js +3 -3
  34. package/build/data/mods/gen6/formats-data.js.map +1 -1
  35. package/build/data/mods/gen6/items.js +24 -0
  36. package/build/data/mods/gen6/items.js.map +1 -1
  37. package/build/data/mods/gen7/abilities.js +31 -0
  38. package/build/data/mods/gen7/abilities.js.map +1 -1
  39. package/build/data/mods/gen7/items.js +12 -0
  40. package/build/data/mods/gen7/items.js.map +1 -1
  41. package/build/data/mods/gen7/moves.js +14 -0
  42. package/build/data/mods/gen7/moves.js.map +1 -1
  43. package/build/data/moves.js +45 -19
  44. package/build/data/moves.js.map +1 -1
  45. package/build/data/rulesets.js +72 -1
  46. package/build/data/rulesets.js.map +1 -1
  47. package/build/data/tags.js +3 -3
  48. package/build/data/tags.js.map +1 -1
  49. package/build/data/text/moves.js +8 -8
  50. package/build/data/text/moves.js.map +1 -1
  51. package/build/sim/battle-queue.d.ts +2 -2
  52. package/build/sim/battle-queue.js +8 -0
  53. package/build/sim/battle-queue.js.map +1 -1
  54. package/build/sim/battle.js +42 -2
  55. package/build/sim/battle.js.map +1 -1
  56. package/build/sim/dex-conditions.d.ts +2 -2
  57. package/build/sim/dex-conditions.js +1 -0
  58. package/build/sim/dex-conditions.js.map +1 -1
  59. package/build/sim/dex-data.js +1 -0
  60. package/build/sim/dex-data.js.map +1 -1
  61. package/build/sim/dex-formats.js +1 -0
  62. package/build/sim/dex-formats.js.map +1 -1
  63. package/build/sim/dex-items.js +1 -0
  64. package/build/sim/dex-items.js.map +1 -1
  65. package/build/sim/dex-moves.d.ts +1 -0
  66. package/build/sim/dex-moves.js +1 -0
  67. package/build/sim/dex-moves.js.map +1 -1
  68. package/build/sim/dex-species.js +1 -0
  69. package/build/sim/dex-species.js.map +1 -1
  70. package/build/sim/pokemon.js +18 -5
  71. package/build/sim/pokemon.js.map +1 -1
  72. package/build/sim/team-validator.js +1 -1
  73. package/build/sim/team-validator.js.map +1 -1
  74. package/config/formats.ts +169 -154
  75. package/data/abilities.ts +2 -2
  76. package/data/aliases.ts +2 -2
  77. package/data/conditions.ts +4 -1
  78. package/data/formats-data.ts +2 -2
  79. package/data/items.ts +4 -2
  80. package/data/learnsets.ts +5 -4
  81. package/data/mods/gen1/formats-data.ts +38 -36
  82. package/data/mods/gen1/scripts.ts +1 -1
  83. package/data/mods/gen2/items.ts +28 -0
  84. package/data/mods/gen2/moves.ts +2 -2
  85. package/data/mods/gen3/items.ts +28 -0
  86. package/data/mods/gen4/abilities.ts +20 -0
  87. package/data/mods/gen4/items.ts +28 -0
  88. package/data/mods/gen4/moves.ts +14 -7
  89. package/data/mods/gen5/items.ts +0 -4
  90. package/data/mods/gen6/formats-data.ts +3 -3
  91. package/data/mods/gen6/items.ts +24 -0
  92. package/data/mods/gen7/abilities.ts +31 -0
  93. package/data/mods/gen7/items.ts +12 -0
  94. package/data/mods/gen7/moves.ts +14 -0
  95. package/data/moves.ts +45 -18
  96. package/data/rulesets.ts +71 -1
  97. package/data/tags.ts +3 -3
  98. package/data/text/moves.ts +8 -8
  99. package/package.json +3 -3
  100. package/sim/battle-queue.ts +10 -2
  101. package/sim/battle.ts +39 -2
  102. package/sim/dex-conditions.ts +7 -2
  103. package/sim/dex-data.ts +1 -0
  104. package/sim/dex-formats.ts +1 -0
  105. package/sim/dex-items.ts +1 -0
  106. package/sim/dex-moves.ts +2 -0
  107. package/sim/dex-species.ts +1 -0
  108. package/sim/pokemon.ts +19 -7
  109. package/sim/team-validator.ts +1 -1
@@ -2245,7 +2245,7 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
2245
2245
  doublesTier: "NFE",
2246
2246
  },
2247
2247
  infernape: {
2248
- randomBattleMoves: ["closecombat", "earthquake", "fireblast", "flareblitz", "grassknot", "machpunch", "nastyplot", "stealthrock", "stoneedge", "uturn", "vacuumwave"],
2248
+ randomBattleMoves: ["closecombat", "earthquake", "fireblast", "flareblitz", "grassknot", "machpunch", "stealthrock", "stoneedge", "uturn"],
2249
2249
  randomDoubleBattleMoves: ["closecombat", "fakeout", "feint", "flareblitz", "grassknot", "heatwave", "machpunch", "protect", "stoneedge", "taunt", "thunderpunch", "uturn"],
2250
2250
  tier: "UU",
2251
2251
  doublesTier: "DUU",
@@ -3279,8 +3279,8 @@ export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
3279
3279
  tier: "NFE",
3280
3280
  },
3281
3281
  haxorus: {
3282
- randomBattleMoves: ["dragondance", "earthquake", "outrage", "poisonjab", "swordsdance", "taunt"],
3283
- randomDoubleBattleMoves: ["dragonclaw", "dragondance", "earthquake", "poisonjab", "protect", "substitute", "swordsdance", "taunt"],
3282
+ randomBattleMoves: ["dragondance", "earthquake", "outrage", "poisonjab", "swordsdance"],
3283
+ randomDoubleBattleMoves: ["dragonclaw", "dragondance", "earthquake", "poisonjab", "protect", "substitute", "swordsdance"],
3284
3284
  tier: "UU",
3285
3285
  doublesTier: "(DUU)",
3286
3286
  },
@@ -25,6 +25,10 @@ export const Items: {[k: string]: ModdedItemData} = {
25
25
  inherit: true,
26
26
  isNonstandard: null,
27
27
  },
28
+ fastball: {
29
+ inherit: true,
30
+ isNonstandard: "Unobtainable",
31
+ },
28
32
  figyberry: {
29
33
  inherit: true,
30
34
  onUpdate(pokemon) {
@@ -39,6 +43,10 @@ export const Items: {[k: string]: ModdedItemData} = {
39
43
  }
40
44
  },
41
45
  },
46
+ heavyball: {
47
+ inherit: true,
48
+ isNonstandard: "Unobtainable",
49
+ },
42
50
  iapapaberry: {
43
51
  inherit: true,
44
52
  onUpdate(pokemon) {
@@ -63,6 +71,10 @@ export const Items: {[k: string]: ModdedItemData} = {
63
71
  }
64
72
  },
65
73
  },
74
+ levelball: {
75
+ inherit: true,
76
+ isNonstandard: "Unobtainable",
77
+ },
66
78
  lifeorb: {
67
79
  inherit: true,
68
80
  onAfterMoveSecondarySelf(source, target, move) {
@@ -71,6 +83,14 @@ export const Items: {[k: string]: ModdedItemData} = {
71
83
  }
72
84
  },
73
85
  },
86
+ loveball: {
87
+ inherit: true,
88
+ isNonstandard: "Unobtainable",
89
+ },
90
+ lureball: {
91
+ inherit: true,
92
+ isNonstandard: "Unobtainable",
93
+ },
74
94
  machobrace: {
75
95
  inherit: true,
76
96
  isNonstandard: null,
@@ -93,6 +113,10 @@ export const Items: {[k: string]: ModdedItemData} = {
93
113
  inherit: true,
94
114
  isNonstandard: null,
95
115
  },
116
+ moonball: {
117
+ inherit: true,
118
+ isNonstandard: "Unobtainable",
119
+ },
96
120
  nanabberry: {
97
121
  inherit: true,
98
122
  isNonstandard: null,
@@ -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) {
@@ -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,
package/data/moves.ts CHANGED
@@ -1006,6 +1006,13 @@ export const Moves: {[moveid: string]: MoveData} = {
1006
1006
  pp: 40,
1007
1007
  priority: 0,
1008
1008
  flags: {},
1009
+ onHit(target) {
1010
+ if (!this.canSwitch(target.side)) {
1011
+ this.attrLastMove('[still]');
1012
+ this.add('-fail', target);
1013
+ return this.NOT_FAIL;
1014
+ }
1015
+ },
1009
1016
  self: {
1010
1017
  onHit(source) {
1011
1018
  source.skipBeforeSwitchOutEventFlag = true;
@@ -1028,7 +1035,7 @@ export const Moves: {[moveid: string]: MoveData} = {
1028
1035
  pp: 15,
1029
1036
  priority: -3,
1030
1037
  flags: {bullet: 1, protect: 1},
1031
- beforeTurnCallback(pokemon) {
1038
+ priorityChargeCallback(pokemon) {
1032
1039
  pokemon.addVolatile('beakblast');
1033
1040
  },
1034
1041
  condition: {
@@ -2633,7 +2640,7 @@ export const Moves: {[moveid: string]: MoveData} = {
2633
2640
  beforeTurnCallback(pokemon) {
2634
2641
  pokemon.addVolatile('counter');
2635
2642
  },
2636
- onTryHit(target, source, move) {
2643
+ onTry(source) {
2637
2644
  if (!source.volatiles['counter']) return false;
2638
2645
  if (source.volatiles['counter'].slot === null) return false;
2639
2646
  },
@@ -3085,6 +3092,7 @@ export const Moves: {[moveid: string]: MoveData} = {
3085
3092
  volatileStatus: 'defensecurl',
3086
3093
  condition: {
3087
3094
  noCopy: true,
3095
+ onRestart: () => null,
3088
3096
  },
3089
3097
  secondary: null,
3090
3098
  target: "self",
@@ -5383,6 +5391,17 @@ export const Moves: {[moveid: string]: MoveData} = {
5383
5391
  if (!this.runEvent('ChargeMove', attacker, defender, move)) {
5384
5392
  return;
5385
5393
  }
5394
+
5395
+ // In SwSh, Fly's animation leaks the initial target through a camera focus
5396
+ // The animation leak target itself isn't "accurate"; the target it reveals is as if Fly weren't a charge movee
5397
+ // (Fly, like all other charge moves, will actually target slots on its charging turn, relevant for things like Follow Me)
5398
+ // We use a generic single-target move to represent this
5399
+ if (this.gameType === 'doubles' || this.gameType === 'multi') {
5400
+ const animatedTarget = attacker.getMoveTargets(this.dex.getActiveMove('aerialace'), defender).targets[0];
5401
+ if (animatedTarget) {
5402
+ this.hint(`${move.name}'s animation targeted ${animatedTarget.name}`);
5403
+ }
5404
+ }
5386
5405
  attacker.addVolatile('twoturnmove', defender);
5387
5406
  return null;
5388
5407
  },
@@ -5481,11 +5500,11 @@ export const Moves: {[moveid: string]: MoveData} = {
5481
5500
  pp: 20,
5482
5501
  priority: -3,
5483
5502
  flags: {contact: 1, protect: 1, punch: 1},
5484
- beforeTurnCallback(pokemon) {
5503
+ priorityChargeCallback(pokemon) {
5485
5504
  pokemon.addVolatile('focuspunch');
5486
5505
  },
5487
5506
  beforeMoveCallback(pokemon) {
5488
- if (pokemon.volatiles['focuspunch'] && pokemon.volatiles['focuspunch'].lostFocus) {
5507
+ if (pokemon.volatiles['focuspunch']?.lostFocus) {
5489
5508
  this.add('cant', pokemon, 'Focus Punch', 'Focus Punch');
5490
5509
  return true;
5491
5510
  }
@@ -5497,7 +5516,7 @@ export const Moves: {[moveid: string]: MoveData} = {
5497
5516
  },
5498
5517
  onHit(pokemon, source, move) {
5499
5518
  if (move.category !== 'Status') {
5500
- pokemon.volatiles['focuspunch'].lostFocus = true;
5519
+ this.effectState.lostFocus = true;
5501
5520
  }
5502
5521
  },
5503
5522
  onTryAddVolatile(status, pokemon) {
@@ -7800,10 +7819,12 @@ export const Moves: {[moveid: string]: MoveData} = {
7800
7819
  pp: 10,
7801
7820
  priority: 0,
7802
7821
  flags: {snatch: 1, heal: 1},
7803
- onTryHit(pokemon, target, move) {
7804
- if (!this.canSwitch(pokemon.side)) {
7822
+ onHit(target, source, move) {
7823
+ if (!this.canSwitch(target.side)) {
7805
7824
  delete move.selfdestruct;
7806
- return false;
7825
+ this.attrLastMove('[still]');
7826
+ this.add('-fail', target);
7827
+ return this.NOT_FAIL;
7807
7828
  }
7808
7829
  },
7809
7830
  selfdestruct: "ifHit",
@@ -9894,10 +9915,12 @@ export const Moves: {[moveid: string]: MoveData} = {
9894
9915
  pp: 10,
9895
9916
  priority: 0,
9896
9917
  flags: {snatch: 1, heal: 1, dance: 1},
9897
- onTryHit(pokemon, target, move) {
9898
- if (!this.canSwitch(pokemon.side)) {
9918
+ onHit(target, source, move) {
9919
+ if (!this.canSwitch(target.side)) {
9899
9920
  delete move.selfdestruct;
9900
- return false;
9921
+ this.attrLastMove('[still]');
9922
+ this.add('-fail', target);
9923
+ return this.NOT_FAIL;
9901
9924
  }
9902
9925
  },
9903
9926
  selfdestruct: "ifHit",
@@ -10914,7 +10937,7 @@ export const Moves: {[moveid: string]: MoveData} = {
10914
10937
  pp: 10,
10915
10938
  priority: 0,
10916
10939
  flags: {protect: 1, mirror: 1},
10917
- onTryHit(target, source, move) {
10940
+ onTry(source) {
10918
10941
  const lastDamagedBy = source.getLastDamagedBy(true);
10919
10942
  if (lastDamagedBy === undefined || !lastDamagedBy.thisTurn) return false;
10920
10943
  },
@@ -11176,6 +11199,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11176
11199
  volatileStatus: 'minimize',
11177
11200
  condition: {
11178
11201
  noCopy: true,
11202
+ onRestart: () => null,
11179
11203
  onSourceModifyDamage(damage, source, target, move) {
11180
11204
  const boostedMoves = [
11181
11205
  'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault',
@@ -11253,7 +11277,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11253
11277
  beforeTurnCallback(pokemon) {
11254
11278
  pokemon.addVolatile('mirrorcoat');
11255
11279
  },
11256
- onTryHit(target, source, move) {
11280
+ onTry(source) {
11257
11281
  if (!source.volatiles['mirrorcoat']) return false;
11258
11282
  if (source.volatiles['mirrorcoat'].slot === null) return false;
11259
11283
  },
@@ -13467,7 +13491,7 @@ export const Moves: {[moveid: string]: MoveData} = {
13467
13491
  let alreadyAdded = false;
13468
13492
  pokemon.removeVolatile('destinybond');
13469
13493
  for (const source of this.effectState.sources) {
13470
- if (!this.queue.cancelMove(source) || !source.hp) continue;
13494
+ if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp) continue;
13471
13495
  if (!alreadyAdded) {
13472
13496
  this.add('-activate', pokemon, 'move: Pursuit');
13473
13497
  alreadyAdded = true;
@@ -13989,7 +14013,8 @@ export const Moves: {[moveid: string]: MoveData} = {
13989
14013
  }
13990
14014
  },
13991
14015
  onHit(target, source, move) {
13992
- if (!target.setStatus('slp', source, move)) return false;
14016
+ const result = target.setStatus('slp', source, move);
14017
+ if (!result) return result;
13993
14018
  target.statusState.time = 3;
13994
14019
  target.statusState.startTime = 3;
13995
14020
  this.heal(target.maxhp); // Aesthetic only as the healing happens after you fall asleep in-game
@@ -15199,7 +15224,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15199
15224
  pp: 5,
15200
15225
  priority: -3,
15201
15226
  flags: {protect: 1},
15202
- beforeTurnCallback(pokemon) {
15227
+ priorityChargeCallback(pokemon) {
15203
15228
  pokemon.addVolatile('shelltrap');
15204
15229
  },
15205
15230
  onTryMove(pokemon) {
@@ -15216,7 +15241,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15216
15241
  },
15217
15242
  onHit(pokemon, source, move) {
15218
15243
  if (!pokemon.isAlly(source) && move.category === 'Physical') {
15219
- pokemon.volatiles['shelltrap'].gotHit = true;
15244
+ this.effectState.gotHit = true;
15220
15245
  const action = this.queue.willMove(pokemon);
15221
15246
  if (action) {
15222
15247
  this.queue.prioritizeAction(action);
@@ -17945,8 +17970,10 @@ export const Moves: {[moveid: string]: MoveData} = {
17945
17970
  pp: 20,
17946
17971
  priority: -6,
17947
17972
  flags: {},
17973
+ onTry(source) {
17974
+ return !!this.canSwitch(source.side);
17975
+ },
17948
17976
  selfSwitch: true,
17949
- onTryHit: true,
17950
17977
  secondary: null,
17951
17978
  target: "self",
17952
17979
  type: "Psychic",
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} = {
@@ -671,7 +672,7 @@ export const Rulesets: {[k: string]: FormatData} = {
671
672
  name: 'Gravity Sleep Clause',
672
673
  desc: "Bans sleep moves below 100% accuracy, in conjunction with Gravity or Gigantamax Orbeetle",
673
674
  banlist: [
674
- 'Gravity ++ Grass Whistle', 'Gravity ++ Hypnosis', 'Gravity ++ Lovely Kiss', 'Gravity ++ Sing', 'Gravity ++ Sleep Powder',
675
+ 'Gravity ++ Dark Void', 'Gravity ++ Grass Whistle', 'Gravity ++ Hypnosis', 'Gravity ++ Lovely Kiss', 'Gravity ++ Sing', 'Gravity ++ Sleep Powder',
675
676
  ],
676
677
  onValidateTeam(team) {
677
678
  let hasOrbeetle = false;
@@ -1604,6 +1605,75 @@ export const Rulesets: {[k: string]: FormatData} = {
1604
1605
  pokemon.trapped = true;
1605
1606
  },
1606
1607
  },
1608
+ crazyhouserule: {
1609
+ effectType: 'Rule',
1610
+ name: 'Crazyhouse Rule',
1611
+ desc: "Pok\u00e9mon you KO are added to your team and removed from the opponent's, and vice versa.",
1612
+ onValidateRule(value) {
1613
+ if (this.format.gameType === 'doubles' || this.format.gameType === 'triples') {
1614
+ throw new Error(`Crazyhouse Rule currently does not support ${this.format.gameType} battles.`);
1615
+ }
1616
+ const ruleTable = this.ruleTable;
1617
+ const maxTeamSize = ruleTable.pickedTeamSize || ruleTable.maxTeamSize;
1618
+ const numPlayers = (this.format.gameType === 'freeforall' || this.format.gameType === 'multi') ? 4 : 2;
1619
+ const potentialMaxTeamSize = maxTeamSize * numPlayers;
1620
+ if (potentialMaxTeamSize > 24) {
1621
+ 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.`);
1622
+ }
1623
+ },
1624
+ // In order to prevent a case of the clones, housekeeping is needed.
1625
+ // This is especially needed to make sure one side doesn't end up with too many Pokemon.
1626
+ onBeforeSwitchIn(pokemon) {
1627
+ if (this.turn < 1 || !pokemon.side.faintedThisTurn) return;
1628
+ pokemon.side.pokemon = pokemon.side.pokemon.filter(x => !(x.fainted && !x.m.outofplay));
1629
+ for (let i = 0; i < pokemon.side.pokemon.length && i < 24; i++) {
1630
+ pokemon.side.pokemon[i].position = i;
1631
+ }
1632
+ },
1633
+ onFaint(target, source, effect) {
1634
+ if (!target.m.numSwaps) {
1635
+ target.m.numSwaps = 0;
1636
+ }
1637
+ target.m.numSwaps++;
1638
+ if (effect && effect.effectType === 'Move' && source.side.pokemon.length < 24 &&
1639
+ source.side !== target.side && target.m.numSwaps < 4) {
1640
+ const hpCost = this.clampIntRange(Math.floor((target.baseMaxhp * target.m.numSwaps) / 4), 1);
1641
+ // Just in case(tm) and for Shedinja
1642
+ if (hpCost === target.baseMaxhp) {
1643
+ target.m.outofplay = true;
1644
+ return;
1645
+ }
1646
+ source.side.pokemonLeft++;
1647
+ source.side.pokemon.length++;
1648
+
1649
+ // A new Pokemon is created and stuff gets aside akin to a deep clone.
1650
+ // This is because deepClone crashes when side is called recursively.
1651
+ // Until a refactor is made to prevent it, this is the best option to prevent crashes.
1652
+ const newPoke = new Pokemon(target.set, source.side);
1653
+ const newPos = source.side.pokemon.length - 1;
1654
+
1655
+ const doNotCarryOver = [
1656
+ 'fullname', 'side', 'fainted', 'status', 'hp', 'illusion',
1657
+ 'transformed', 'position', 'isActive', 'faintQueued',
1658
+ 'subFainted', 'getHealth', 'getDetails', 'moveSlots', 'ability',
1659
+ ];
1660
+ for (const [key, value] of Object.entries(target)) {
1661
+ if (doNotCarryOver.includes(key)) continue;
1662
+ // @ts-ignore
1663
+ newPoke[key] = value;
1664
+ }
1665
+ newPoke.maxhp = newPoke.baseMaxhp; // for dynamax
1666
+ newPoke.hp = newPoke.baseMaxhp - hpCost;
1667
+ newPoke.clearVolatile();
1668
+ newPoke.position = newPos;
1669
+ source.side.pokemon[newPos] = newPoke;
1670
+ this.add('poke', source.side.pokemon[newPos].side.id, source.side.pokemon[newPos].details, '');
1671
+ this.add('-message', `${target.name} was captured by ${newPoke.side.name}!`);
1672
+ } else {
1673
+ target.m.outofplay = true;
1674
+ }
1675
+ },
1676
+ },
1607
1677
  chimera1v1rule: {
1608
1678
  effectType: 'Rule',
1609
1679
  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
 
@@ -296,7 +296,7 @@ export const MovesText: {[k: string]: MoveText} = {
296
296
  },
297
297
  banefulbunker: {
298
298
  name: "Baneful Bunker",
299
- desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user become poisoned. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
299
+ desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user become poisoned. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
300
300
  shortDesc: "Protects from moves. Contact: poison.",
301
301
  gen7: {
302
302
  desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user become poisoned. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -1159,7 +1159,7 @@ export const MovesText: {[k: string]: MoveText} = {
1159
1159
  },
1160
1160
  detect: {
1161
1161
  name: "Detect",
1162
- desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
1162
+ desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
1163
1163
  shortDesc: "Prevents moves from affecting the user this turn.",
1164
1164
  gen7: {
1165
1165
  desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -1583,7 +1583,7 @@ export const MovesText: {[k: string]: MoveText} = {
1583
1583
  },
1584
1584
  endure: {
1585
1585
  name: "Endure",
1586
- desc: "The user will survive attacks made by other Pokemon during this turn with at least 1 HP. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
1586
+ desc: "The user will survive attacks made by other Pokemon during this turn with at least 1 HP. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
1587
1587
  shortDesc: "User survives attacks this turn with at least 1 HP.",
1588
1588
  gen7: {
1589
1589
  desc: "The user will survive attacks made by other Pokemon during this turn with at least 1 HP. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -3145,7 +3145,7 @@ export const MovesText: {[k: string]: MoveText} = {
3145
3145
  },
3146
3146
  kingsshield: {
3147
3147
  name: "King's Shield",
3148
- desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 1 stage. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
3148
+ desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 1 stage. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
3149
3149
  shortDesc: "Protects from damaging attacks. Contact: -1 Atk.",
3150
3150
  gen7: {
3151
3151
  desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 2 stages. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -4417,7 +4417,7 @@ export const MovesText: {[k: string]: MoveText} = {
4417
4417
  },
4418
4418
  protect: {
4419
4419
  name: "Protect",
4420
- desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
4420
+ desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
4421
4421
  shortDesc: "Prevents moves from affecting the user this turn.",
4422
4422
  gen7: {
4423
4423
  desc: "The user is protected from most attacks made by other Pokemon during this turn. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -4570,7 +4570,7 @@ export const MovesText: {[k: string]: MoveText} = {
4570
4570
  },
4571
4571
  quickguard: {
4572
4572
  name: "Quick Guard",
4573
- desc: "The user and its party members are protected from attacks with original or altered priority greater than 0 made by other Pokemon, including allies, during this turn. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
4573
+ desc: "The user and its party members are protected from attacks with original or altered priority greater than 0 made by other Pokemon, including allies, during this turn. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
4574
4574
  shortDesc: "Protects allies from priority attacks this turn.",
4575
4575
  gen7: {
4576
4576
  desc: "The user and its party members are protected from attacks with original or altered priority greater than 0 made by other Pokemon, including allies, during this turn. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
@@ -5613,7 +5613,7 @@ export const MovesText: {[k: string]: MoveText} = {
5613
5613
  },
5614
5614
  spikyshield: {
5615
5615
  name: "Spiky Shield",
5616
- desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user lose 1/8 of their maximum HP, rounded down. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
5616
+ desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user lose 1/8 of their maximum HP, rounded down. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
5617
5617
  shortDesc: "Protects from moves. Contact: loses 1/8 max HP.",
5618
5618
  gen7: {
5619
5619
  desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon making contact with the user lose 1/8 of their maximum HP, rounded down. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.",
@@ -6701,7 +6701,7 @@ export const MovesText: {[k: string]: MoveText} = {
6701
6701
  },
6702
6702
  wideguard: {
6703
6703
  name: "Wide Guard",
6704
- desc: "The user and its party members are protected from moves made by other Pokemon, including allies, during this turn that target all adjacent foes or all adjacent Pokemon. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
6704
+ desc: "The user and its party members are protected from moves made by other Pokemon, including allies, during this turn that target all adjacent foes or all adjacent Pokemon. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Max Guard, Obstruct, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
6705
6705
  shortDesc: "Protects allies from multi-target moves this turn.",
6706
6706
  gen7: {
6707
6707
  desc: "The user and its party members are protected from moves made by other Pokemon, including allies, during this turn that target all adjacent foes or all adjacent Pokemon. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.5.12",
3
+ "version": "0.5.15",
4
4
  "description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown",
5
5
  "homepage": "https://psim.us",
6
6
  "main": "build/sim/index.js",
@@ -27,11 +27,11 @@
27
27
  "sim"
28
28
  ],
29
29
  "dependencies": {
30
- "@pkmn/sets": "^2.0.0",
30
+ "@pkmn/sets": "^3.0.0",
31
31
  "@pkmn/streams": "^1.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "mocha": "^9.2.2"
34
+ "mocha": "^10.0.0"
35
35
  },
36
36
  "scripts": {
37
37
  "compile": "tsc -p .",
@@ -20,8 +20,8 @@ import type {Battle} from './battle';
20
20
  /** A move action */
21
21
  export interface MoveAction {
22
22
  /** action type */
23
- choice: 'move' | 'beforeTurnMove';
24
- order: 3 | 5 | 200 | 201 | 199;
23
+ choice: 'move' | 'beforeTurnMove' | 'priorityChargeMove';
24
+ order: 3 | 5 | 200 | 201 | 199 | 106;
25
25
  /** priority of the action (lower first) */
26
26
  priority: number;
27
27
  /** fractional priority of the action (lower first) */
@@ -181,6 +181,7 @@ export class BattleQueue {
181
181
  switch: 103,
182
182
  megaEvo: 104,
183
183
  runDynamax: 105,
184
+ priorityChargeMove: 106,
184
185
 
185
186
  shift: 200,
186
187
  // default is 200 (for moves)
@@ -215,6 +216,13 @@ export class BattleQueue {
215
216
  pokemon: action.pokemon,
216
217
  }));
217
218
  }
219
+ if (!action.maxMove && !action.zmove && action.move.priorityChargeCallback) {
220
+ actions.unshift(...this.resolveAction({
221
+ choice: 'priorityChargeMove',
222
+ pokemon: action.pokemon,
223
+ move: action.move,
224
+ }));
225
+ }
218
226
  action.fractionalPriority = this.battle.runEvent('FractionalPriority', action.pokemon, null, action.move, 0);
219
227
  } else if (['switch', 'instaswitch'].includes(action.choice)) {
220
228
  if (typeof action.pokemon.switchFlag === 'string') {