@pkmn/sim 0.5.13 → 0.5.16

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 (98) hide show
  1. package/build/config/formats.js +151 -145
  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 +4 -2
  10. package/build/data/items.js.map +1 -1
  11. package/build/data/learnsets.js +5 -4
  12. package/build/data/learnsets.js.map +1 -1
  13. package/build/data/mods/gen1/formats-data.js +32 -32
  14. package/build/data/mods/gen1/formats-data.js.map +1 -1
  15. package/build/data/mods/gen1/scripts.js +1 -1
  16. package/build/data/mods/gen1/scripts.js.map +1 -1
  17. package/build/data/mods/gen2/items.js +28 -0
  18. package/build/data/mods/gen2/items.js.map +1 -1
  19. package/build/data/mods/gen2/moves.js +2 -5
  20. package/build/data/mods/gen2/moves.js.map +1 -1
  21. package/build/data/mods/gen3/items.js +28 -0
  22. package/build/data/mods/gen3/items.js.map +1 -1
  23. package/build/data/mods/gen3/moves.js +0 -3
  24. package/build/data/mods/gen3/moves.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 +16 -9
  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/items.js +24 -0
  34. package/build/data/mods/gen6/items.js.map +1 -1
  35. package/build/data/mods/gen7/abilities.js +31 -0
  36. package/build/data/mods/gen7/abilities.js.map +1 -1
  37. package/build/data/mods/gen7/items.js +12 -0
  38. package/build/data/mods/gen7/items.js.map +1 -1
  39. package/build/data/mods/gen7/moves.js +14 -0
  40. package/build/data/mods/gen7/moves.js.map +1 -1
  41. package/build/data/moves.js +45 -20
  42. package/build/data/moves.js.map +1 -1
  43. package/build/data/rulesets.js +71 -0
  44. package/build/data/rulesets.js.map +1 -1
  45. package/build/data/tags.js +3 -3
  46. package/build/data/tags.js.map +1 -1
  47. package/build/data/text/moves.js +8 -8
  48. package/build/data/text/moves.js.map +1 -1
  49. package/build/sim/battle-actions.js +3 -3
  50. package/build/sim/battle-actions.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 +46 -6
  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.map +1 -1
  58. package/build/sim/dex-data.js +0 -1
  59. package/build/sim/dex-data.js.map +1 -1
  60. package/build/sim/dex-moves.d.ts +2 -1
  61. package/build/sim/dex-moves.js.map +1 -1
  62. package/build/sim/pokemon.js +18 -5
  63. package/build/sim/pokemon.js.map +1 -1
  64. package/build/sim/team-validator.js +1 -1
  65. package/build/sim/team-validator.js.map +1 -1
  66. package/config/formats.ts +156 -150
  67. package/data/aliases.ts +2 -2
  68. package/data/conditions.ts +4 -1
  69. package/data/formats-data.ts +3 -3
  70. package/data/items.ts +4 -2
  71. package/data/learnsets.ts +5 -4
  72. package/data/mods/gen1/formats-data.ts +32 -32
  73. package/data/mods/gen1/scripts.ts +1 -1
  74. package/data/mods/gen2/items.ts +28 -0
  75. package/data/mods/gen2/moves.ts +2 -5
  76. package/data/mods/gen3/items.ts +28 -0
  77. package/data/mods/gen3/moves.ts +0 -3
  78. package/data/mods/gen4/abilities.ts +20 -0
  79. package/data/mods/gen4/items.ts +28 -0
  80. package/data/mods/gen4/moves.ts +15 -10
  81. package/data/mods/gen5/items.ts +0 -4
  82. package/data/mods/gen6/items.ts +24 -0
  83. package/data/mods/gen7/abilities.ts +31 -0
  84. package/data/mods/gen7/items.ts +12 -0
  85. package/data/mods/gen7/moves.ts +14 -0
  86. package/data/moves.ts +45 -19
  87. package/data/rulesets.ts +70 -0
  88. package/data/tags.ts +3 -3
  89. package/data/text/moves.ts +8 -8
  90. package/package.json +2 -2
  91. package/sim/battle-actions.ts +3 -4
  92. package/sim/battle-queue.ts +10 -2
  93. package/sim/battle.ts +43 -6
  94. package/sim/dex-conditions.ts +6 -2
  95. package/sim/dex-data.ts +0 -1
  96. package/sim/dex-moves.ts +2 -1
  97. package/sim/pokemon.ts +19 -7
  98. package/sim/team-validator.ts +1 -1
@@ -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",
@@ -4791,7 +4799,6 @@ export const Moves: {[moveid: string]: MoveData} = {
4791
4799
  pp: 5,
4792
4800
  priority: 0,
4793
4801
  flags: {protect: 1},
4794
- selfdestruct: "ifHit",
4795
4802
  secondary: null,
4796
4803
  target: "normal",
4797
4804
  type: "Fighting",
@@ -5383,6 +5390,17 @@ export const Moves: {[moveid: string]: MoveData} = {
5383
5390
  if (!this.runEvent('ChargeMove', attacker, defender, move)) {
5384
5391
  return;
5385
5392
  }
5393
+
5394
+ // In SwSh, Fly's animation leaks the initial target through a camera focus
5395
+ // The animation leak target itself isn't "accurate"; the target it reveals is as if Fly weren't a charge movee
5396
+ // (Fly, like all other charge moves, will actually target slots on its charging turn, relevant for things like Follow Me)
5397
+ // We use a generic single-target move to represent this
5398
+ if (this.gameType === 'doubles' || this.gameType === 'multi') {
5399
+ const animatedTarget = attacker.getMoveTargets(this.dex.getActiveMove('aerialace'), defender).targets[0];
5400
+ if (animatedTarget) {
5401
+ this.hint(`${move.name}'s animation targeted ${animatedTarget.name}`);
5402
+ }
5403
+ }
5386
5404
  attacker.addVolatile('twoturnmove', defender);
5387
5405
  return null;
5388
5406
  },
@@ -5481,11 +5499,11 @@ export const Moves: {[moveid: string]: MoveData} = {
5481
5499
  pp: 20,
5482
5500
  priority: -3,
5483
5501
  flags: {contact: 1, protect: 1, punch: 1},
5484
- beforeTurnCallback(pokemon) {
5502
+ priorityChargeCallback(pokemon) {
5485
5503
  pokemon.addVolatile('focuspunch');
5486
5504
  },
5487
5505
  beforeMoveCallback(pokemon) {
5488
- if (pokemon.volatiles['focuspunch'] && pokemon.volatiles['focuspunch'].lostFocus) {
5506
+ if (pokemon.volatiles['focuspunch']?.lostFocus) {
5489
5507
  this.add('cant', pokemon, 'Focus Punch', 'Focus Punch');
5490
5508
  return true;
5491
5509
  }
@@ -5497,7 +5515,7 @@ export const Moves: {[moveid: string]: MoveData} = {
5497
5515
  },
5498
5516
  onHit(pokemon, source, move) {
5499
5517
  if (move.category !== 'Status') {
5500
- pokemon.volatiles['focuspunch'].lostFocus = true;
5518
+ this.effectState.lostFocus = true;
5501
5519
  }
5502
5520
  },
5503
5521
  onTryAddVolatile(status, pokemon) {
@@ -7800,10 +7818,12 @@ export const Moves: {[moveid: string]: MoveData} = {
7800
7818
  pp: 10,
7801
7819
  priority: 0,
7802
7820
  flags: {snatch: 1, heal: 1},
7803
- onTryHit(pokemon, target, move) {
7804
- if (!this.canSwitch(pokemon.side)) {
7821
+ onHit(target, source, move) {
7822
+ if (!this.canSwitch(source.side)) {
7805
7823
  delete move.selfdestruct;
7806
- return false;
7824
+ this.attrLastMove('[still]');
7825
+ this.add('-fail', source);
7826
+ return this.NOT_FAIL;
7807
7827
  }
7808
7828
  },
7809
7829
  selfdestruct: "ifHit",
@@ -9894,10 +9914,12 @@ export const Moves: {[moveid: string]: MoveData} = {
9894
9914
  pp: 10,
9895
9915
  priority: 0,
9896
9916
  flags: {snatch: 1, heal: 1, dance: 1},
9897
- onTryHit(pokemon, target, move) {
9898
- if (!this.canSwitch(pokemon.side)) {
9917
+ onHit(target, source, move) {
9918
+ if (!this.canSwitch(source.side)) {
9899
9919
  delete move.selfdestruct;
9900
- return false;
9920
+ this.attrLastMove('[still]');
9921
+ this.add('-fail', source);
9922
+ return this.NOT_FAIL;
9901
9923
  }
9902
9924
  },
9903
9925
  selfdestruct: "ifHit",
@@ -10914,7 +10936,7 @@ export const Moves: {[moveid: string]: MoveData} = {
10914
10936
  pp: 10,
10915
10937
  priority: 0,
10916
10938
  flags: {protect: 1, mirror: 1},
10917
- onTryHit(target, source, move) {
10939
+ onTry(source) {
10918
10940
  const lastDamagedBy = source.getLastDamagedBy(true);
10919
10941
  if (lastDamagedBy === undefined || !lastDamagedBy.thisTurn) return false;
10920
10942
  },
@@ -11176,6 +11198,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11176
11198
  volatileStatus: 'minimize',
11177
11199
  condition: {
11178
11200
  noCopy: true,
11201
+ onRestart: () => null,
11179
11202
  onSourceModifyDamage(damage, source, target, move) {
11180
11203
  const boostedMoves = [
11181
11204
  'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault',
@@ -11253,7 +11276,7 @@ export const Moves: {[moveid: string]: MoveData} = {
11253
11276
  beforeTurnCallback(pokemon) {
11254
11277
  pokemon.addVolatile('mirrorcoat');
11255
11278
  },
11256
- onTryHit(target, source, move) {
11279
+ onTry(source) {
11257
11280
  if (!source.volatiles['mirrorcoat']) return false;
11258
11281
  if (source.volatiles['mirrorcoat'].slot === null) return false;
11259
11282
  },
@@ -13467,7 +13490,7 @@ export const Moves: {[moveid: string]: MoveData} = {
13467
13490
  let alreadyAdded = false;
13468
13491
  pokemon.removeVolatile('destinybond');
13469
13492
  for (const source of this.effectState.sources) {
13470
- if (!this.queue.cancelMove(source) || !source.hp) continue;
13493
+ if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp) continue;
13471
13494
  if (!alreadyAdded) {
13472
13495
  this.add('-activate', pokemon, 'move: Pursuit');
13473
13496
  alreadyAdded = true;
@@ -13989,7 +14012,8 @@ export const Moves: {[moveid: string]: MoveData} = {
13989
14012
  }
13990
14013
  },
13991
14014
  onHit(target, source, move) {
13992
- if (!target.setStatus('slp', source, move)) return false;
14015
+ const result = target.setStatus('slp', source, move);
14016
+ if (!result) return result;
13993
14017
  target.statusState.time = 3;
13994
14018
  target.statusState.startTime = 3;
13995
14019
  this.heal(target.maxhp); // Aesthetic only as the healing happens after you fall asleep in-game
@@ -15199,7 +15223,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15199
15223
  pp: 5,
15200
15224
  priority: -3,
15201
15225
  flags: {protect: 1},
15202
- beforeTurnCallback(pokemon) {
15226
+ priorityChargeCallback(pokemon) {
15203
15227
  pokemon.addVolatile('shelltrap');
15204
15228
  },
15205
15229
  onTryMove(pokemon) {
@@ -15216,7 +15240,7 @@ export const Moves: {[moveid: string]: MoveData} = {
15216
15240
  },
15217
15241
  onHit(pokemon, source, move) {
15218
15242
  if (!pokemon.isAlly(source) && move.category === 'Physical') {
15219
- pokemon.volatiles['shelltrap'].gotHit = true;
15243
+ this.effectState.gotHit = true;
15220
15244
  const action = this.queue.willMove(pokemon);
15221
15245
  if (action) {
15222
15246
  this.queue.prioritizeAction(action);
@@ -17945,8 +17969,10 @@ export const Moves: {[moveid: string]: MoveData} = {
17945
17969
  pp: 20,
17946
17970
  priority: -6,
17947
17971
  flags: {},
17972
+ onTry(source) {
17973
+ return !!this.canSwitch(source.side);
17974
+ },
17948
17975
  selfSwitch: true,
17949
- onTryHit: true,
17950
17976
  secondary: null,
17951
17977
  target: "self",
17952
17978
  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} = {
@@ -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.13",
3
+ "version": "0.5.16",
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",
@@ -31,7 +31,7 @@
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 .",
@@ -1124,9 +1124,6 @@ export class BattleActions {
1124
1124
  continue;
1125
1125
  }
1126
1126
  damage[i] = curDamage;
1127
- if (move.selfdestruct === 'ifHit') {
1128
- this.battle.faint(source, source, move);
1129
- }
1130
1127
  }
1131
1128
  return damage;
1132
1129
  }
@@ -1230,6 +1227,9 @@ export class BattleActions {
1230
1227
  }
1231
1228
  }
1232
1229
  }
1230
+ if (moveData.selfdestruct === 'ifHit' && damage[i] !== false) {
1231
+ this.battle.faint(source, source, move);
1232
+ }
1233
1233
  if (moveData.selfSwitch) {
1234
1234
  if (this.battle.canSwitch(source.side)) {
1235
1235
  didSomething = true;
@@ -1243,7 +1243,6 @@ export class BattleActions {
1243
1243
  didAnything = this.combineResults(didAnything, didSomething);
1244
1244
  }
1245
1245
 
1246
-
1247
1246
  if (!didAnything && didAnything !== 0 && !moveData.self && !moveData.selfdestruct) {
1248
1247
  if (!isSelf && !isSecondary) {
1249
1248
  if (didAnything === false) {
@@ -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') {
package/sim/battle.ts CHANGED
@@ -1419,6 +1419,20 @@ export class Battle {
1419
1419
  this.turn++;
1420
1420
  this.lastSuccessfulMoveThisTurn = null;
1421
1421
 
1422
+ const dynamaxEnding: Pokemon[] = [];
1423
+ for (const pokemon of this.getAllActive()) {
1424
+ if (pokemon.volatiles['dynamax']?.turns === 3) {
1425
+ dynamaxEnding.push(pokemon);
1426
+ }
1427
+ }
1428
+ if (dynamaxEnding.length > 1) {
1429
+ this.updateSpeed();
1430
+ this.speedSort(dynamaxEnding);
1431
+ }
1432
+ for (const pokemon of dynamaxEnding) {
1433
+ pokemon.removeVolatile('dynamax');
1434
+ }
1435
+
1422
1436
  const trappedBySide: boolean[] = [];
1423
1437
  const stalenessBySide: ('internal' | 'external' | undefined)[] = [];
1424
1438
  for (const side of this.sides) {
@@ -1548,6 +1562,23 @@ export class Battle {
1548
1562
  if (this.gen === 2) this.quickClawRoll = this.randomChance(60, 256);
1549
1563
  if (this.gen === 3) this.quickClawRoll = this.randomChance(1, 5);
1550
1564
 
1565
+ // Crazyhouse Progress checker because sidebars has trouble keeping track of Pokemon.
1566
+ // Please remove me once there is client support.
1567
+ if (this.ruleTable.has('crazyhouserule')) {
1568
+ for (const side of this.sides) {
1569
+ let buf = `raw|${side.name}'s team:<br />`;
1570
+ for (const pokemon of side.pokemon) {
1571
+ if (!buf.endsWith('<br />')) buf += '/</span>&#8203;';
1572
+ if (pokemon.fainted) {
1573
+ buf += `<span style="white-space:nowrap;"><span style="opacity:.3"><psicon pokemon="${pokemon.species.id}" /></span>`;
1574
+ } else {
1575
+ buf += `<span style="white-space:nowrap"><psicon pokemon="${pokemon.species.id}" />`;
1576
+ }
1577
+ }
1578
+ this.add(`${buf}</span>`);
1579
+ }
1580
+ }
1581
+
1551
1582
  this.makeRequest('move');
1552
1583
  }
1553
1584
 
@@ -2232,11 +2263,11 @@ export class Battle {
2232
2263
  if (this.activePerHalf > 2) {
2233
2264
  if (move.target === 'adjacentFoe' || move.target === 'normal' || move.target === 'randomNormal') {
2234
2265
  // even if a move can target an ally, auto-resolution will never make it target an ally
2235
- // i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your all
2266
+ // i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your ally
2236
2267
  const adjacentFoes = pokemon.adjacentFoes();
2237
2268
  if (adjacentFoes.length) return this.sample(adjacentFoes);
2238
- // no valid target at all, return a possibly-fainted foe for any possible redirection
2239
- return pokemon.side.foe.active[0];
2269
+ // no valid target at all, return slot directly across for any possible redirection
2270
+ return pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
2240
2271
  }
2241
2272
  }
2242
2273
  return pokemon.side.randomFoe() || pokemon.side.foe.active[0];
@@ -2422,7 +2453,7 @@ export class Battle {
2422
2453
  action.pokemon.side.dynamaxUsed = true;
2423
2454
  if (action.pokemon.side.allySide) action.pokemon.side.allySide.dynamaxUsed = true;
2424
2455
  break;
2425
- case 'beforeTurnMove': {
2456
+ case 'beforeTurnMove':
2426
2457
  if (!action.pokemon.isActive) return false;
2427
2458
  if (action.pokemon.fainted) return false;
2428
2459
  this.debug('before turn callback: ' + action.move.id);
@@ -2431,7 +2462,13 @@ export class Battle {
2431
2462
  if (!action.move.beforeTurnCallback) throw new Error(`beforeTurnMove has no beforeTurnCallback`);
2432
2463
  action.move.beforeTurnCallback.call(this, action.pokemon, target);
2433
2464
  break;
2434
- }
2465
+ case 'priorityChargeMove':
2466
+ if (!action.pokemon.isActive) return false;
2467
+ if (action.pokemon.fainted) return false;
2468
+ this.debug('priority charge callback: ' + action.move.id);
2469
+ if (!action.move.priorityChargeCallback) throw new Error(`priorityChargeMove has no priorityChargeCallback`);
2470
+ action.move.priorityChargeCallback.call(this, action.pokemon);
2471
+ break;
2435
2472
 
2436
2473
  case 'event':
2437
2474
  this.runEvent(action.event!, action.pokemon);
@@ -2587,7 +2624,7 @@ export class Battle {
2587
2624
 
2588
2625
  if (this.gen < 5) this.eachEvent('Update');
2589
2626
 
2590
- if (this.gen >= 8 && this.queue.peek()?.choice === 'move') {
2627
+ if (this.gen >= 8 && (this.queue.peek()?.choice === 'move' || this.queue.peek()?.choice === 'runDynamax')) {
2591
2628
  // In gen 8, speed is updated dynamically so update the queue's speed properties and sort it.
2592
2629
  this.updateSpeed();
2593
2630
  for (const queueAction of this.queue.list) {
@@ -635,8 +635,12 @@ export class Condition extends BasicEffect implements
635
635
  declare readonly durationCallback?: (this: Battle, target: Pokemon, source: Pokemon, effect: Effect | null) => number;
636
636
  declare readonly onCopy?: (this: Battle, pokemon: Pokemon) => void;
637
637
  declare readonly onEnd?: (this: Battle, target: Pokemon) => void;
638
- declare readonly onRestart?: (this: Battle, target: Pokemon, source: Pokemon, sourceEffect: Effect) => void;
639
- declare readonly onStart?: (this: Battle, target: Pokemon, source: Pokemon, sourceEffect: Effect) => void;
638
+ declare readonly onRestart?: (
639
+ this: Battle, target: Pokemon, source: Pokemon, sourceEffect: Effect
640
+ ) => boolean | null | void;
641
+ declare readonly onStart?: (
642
+ this: Battle, target: Pokemon, source: Pokemon, sourceEffect: Effect
643
+ ) => boolean | null | void;
640
644
 
641
645
  constructor(data: AnyObject) {
642
646
  super(data);