@pkmn/sim 0.9.27 → 0.9.28

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 (169) hide show
  1. package/build/cjs/config/formats.js +51 -51
  2. package/build/cjs/config/formats.js.map +1 -1
  3. package/build/cjs/data/abilities.js +83 -84
  4. package/build/cjs/data/abilities.js.map +1 -1
  5. package/build/cjs/data/aliases.js +3 -2
  6. package/build/cjs/data/aliases.js.map +1 -1
  7. package/build/cjs/data/conditions.js +3 -3
  8. package/build/cjs/data/conditions.js.map +1 -1
  9. package/build/cjs/data/formats-data.js +6 -6
  10. package/build/cjs/data/formats-data.js.map +1 -1
  11. package/build/cjs/data/items.js +55 -25
  12. package/build/cjs/data/items.js.map +1 -1
  13. package/build/cjs/data/legality.js +275 -23
  14. package/build/cjs/data/legality.js.map +1 -1
  15. package/build/cjs/data/mods/gen1/moves.js.map +1 -1
  16. package/build/cjs/data/mods/gen1/scripts.js +2 -0
  17. package/build/cjs/data/mods/gen1/scripts.js.map +1 -1
  18. package/build/cjs/data/mods/gen3/abilities.js +0 -2
  19. package/build/cjs/data/mods/gen3/abilities.js.map +1 -1
  20. package/build/cjs/data/mods/gen3/moves.js.map +1 -1
  21. package/build/cjs/data/mods/gen4/abilities.js +1 -1
  22. package/build/cjs/data/mods/gen4/abilities.js.map +1 -1
  23. package/build/cjs/data/mods/gen4/moves.js +31 -0
  24. package/build/cjs/data/mods/gen4/moves.js.map +1 -1
  25. package/build/cjs/data/mods/gen4/scripts.js +26 -0
  26. package/build/cjs/data/mods/gen4/scripts.js.map +1 -1
  27. package/build/cjs/data/mods/gen5/formats-data.js +1 -1
  28. package/build/cjs/data/mods/gen5/formats-data.js.map +1 -1
  29. package/build/cjs/data/mods/gen6/moves.js.map +1 -1
  30. package/build/cjs/data/mods/gen8/moves.js +1 -1
  31. package/build/cjs/data/mods/gen8/moves.js.map +1 -1
  32. package/build/cjs/data/moves.js +15 -9
  33. package/build/cjs/data/moves.js.map +1 -1
  34. package/build/cjs/data/pokemongo.js +1 -1
  35. package/build/cjs/data/text/moves.js +1 -1
  36. package/build/cjs/data/text/moves.js.map +1 -1
  37. package/build/cjs/lib/utils.d.ts +2 -0
  38. package/build/cjs/lib/utils.js +1 -1
  39. package/build/cjs/lib/utils.js.map +1 -1
  40. package/build/cjs/sim/battle-actions.js +25 -36
  41. package/build/cjs/sim/battle-actions.js.map +1 -1
  42. package/build/cjs/sim/battle-queue.d.ts +1 -1
  43. package/build/cjs/sim/battle-queue.js +0 -2
  44. package/build/cjs/sim/battle-queue.js.map +1 -1
  45. package/build/cjs/sim/battle-stream.js +2 -3
  46. package/build/cjs/sim/battle-stream.js.map +1 -1
  47. package/build/cjs/sim/battle.d.ts +13 -7
  48. package/build/cjs/sim/battle.js +185 -60
  49. package/build/cjs/sim/battle.js.map +1 -1
  50. package/build/cjs/sim/dex-abilities.d.ts +0 -1
  51. package/build/cjs/sim/dex-abilities.js +2 -2
  52. package/build/cjs/sim/dex-abilities.js.map +1 -1
  53. package/build/cjs/sim/dex-conditions.d.ts +11 -3
  54. package/build/cjs/sim/dex-conditions.js +2 -2
  55. package/build/cjs/sim/dex-conditions.js.map +1 -1
  56. package/build/cjs/sim/dex-data.js +7 -7
  57. package/build/cjs/sim/dex-data.js.map +1 -1
  58. package/build/cjs/sim/dex-formats.d.ts +2 -1
  59. package/build/cjs/sim/dex-formats.js.map +1 -1
  60. package/build/cjs/sim/dex-items.d.ts +3 -1
  61. package/build/cjs/sim/dex-items.js +3 -2
  62. package/build/cjs/sim/dex-items.js.map +1 -1
  63. package/build/cjs/sim/dex-moves.js +4 -4
  64. package/build/cjs/sim/dex-moves.js.map +1 -1
  65. package/build/cjs/sim/exported-global-types.d.ts +6 -0
  66. package/build/cjs/sim/field.js +9 -9
  67. package/build/cjs/sim/field.js.map +1 -1
  68. package/build/cjs/sim/global-types.d.ts +6 -0
  69. package/build/cjs/sim/pokemon.d.ts +8 -4
  70. package/build/cjs/sim/pokemon.js +61 -46
  71. package/build/cjs/sim/pokemon.js.map +1 -1
  72. package/build/cjs/sim/prng.d.ts +7 -4
  73. package/build/cjs/sim/prng.js +67 -16
  74. package/build/cjs/sim/prng.js.map +1 -1
  75. package/build/cjs/sim/side.js +9 -6
  76. package/build/cjs/sim/side.js.map +1 -1
  77. package/build/cjs/sim/team-validator.js +16 -17
  78. package/build/cjs/sim/team-validator.js.map +1 -1
  79. package/build/cjs/sim/tools/exhaustive-runner.js +3 -4
  80. package/build/cjs/sim/tools/exhaustive-runner.js.map +1 -1
  81. package/build/cjs/sim/tools/random-player-ai.js +1 -1
  82. package/build/cjs/sim/tools/random-player-ai.js.map +1 -1
  83. package/build/cjs/sim/tools/runner.js +2 -3
  84. package/build/cjs/sim/tools/runner.js.map +1 -1
  85. package/build/esm/config/formats.mjs +51 -51
  86. package/build/esm/config/formats.mjs.map +1 -1
  87. package/build/esm/data/abilities.mjs +83 -84
  88. package/build/esm/data/abilities.mjs.map +1 -1
  89. package/build/esm/data/aliases.mjs +3 -2
  90. package/build/esm/data/aliases.mjs.map +1 -1
  91. package/build/esm/data/conditions.mjs +3 -3
  92. package/build/esm/data/conditions.mjs.map +1 -1
  93. package/build/esm/data/formats-data.mjs +6 -6
  94. package/build/esm/data/formats-data.mjs.map +1 -1
  95. package/build/esm/data/items.mjs +55 -25
  96. package/build/esm/data/items.mjs.map +1 -1
  97. package/build/esm/data/legality.mjs +275 -23
  98. package/build/esm/data/legality.mjs.map +1 -1
  99. package/build/esm/data/mods/gen1/moves.mjs.map +1 -1
  100. package/build/esm/data/mods/gen1/scripts.mjs +2 -0
  101. package/build/esm/data/mods/gen1/scripts.mjs.map +1 -1
  102. package/build/esm/data/mods/gen3/abilities.mjs +0 -2
  103. package/build/esm/data/mods/gen3/abilities.mjs.map +1 -1
  104. package/build/esm/data/mods/gen3/moves.mjs.map +1 -1
  105. package/build/esm/data/mods/gen4/abilities.mjs +1 -1
  106. package/build/esm/data/mods/gen4/abilities.mjs.map +1 -1
  107. package/build/esm/data/mods/gen4/moves.mjs +31 -0
  108. package/build/esm/data/mods/gen4/moves.mjs.map +1 -1
  109. package/build/esm/data/mods/gen4/scripts.mjs +26 -0
  110. package/build/esm/data/mods/gen4/scripts.mjs.map +1 -1
  111. package/build/esm/data/mods/gen5/formats-data.mjs +1 -1
  112. package/build/esm/data/mods/gen5/formats-data.mjs.map +1 -1
  113. package/build/esm/data/mods/gen6/moves.mjs.map +1 -1
  114. package/build/esm/data/mods/gen8/moves.mjs +1 -1
  115. package/build/esm/data/mods/gen8/moves.mjs.map +1 -1
  116. package/build/esm/data/moves.mjs +15 -9
  117. package/build/esm/data/moves.mjs.map +1 -1
  118. package/build/esm/data/pokemongo.mjs +1 -1
  119. package/build/esm/data/text/moves.mjs +1 -1
  120. package/build/esm/data/text/moves.mjs.map +1 -1
  121. package/build/esm/lib/utils.d.mts +2 -0
  122. package/build/esm/lib/utils.mjs +1 -1
  123. package/build/esm/lib/utils.mjs.map +1 -1
  124. package/build/esm/sim/battle-actions.mjs +25 -36
  125. package/build/esm/sim/battle-actions.mjs.map +1 -1
  126. package/build/esm/sim/battle-queue.d.mts +1 -1
  127. package/build/esm/sim/battle-queue.mjs +0 -2
  128. package/build/esm/sim/battle-queue.mjs.map +1 -1
  129. package/build/esm/sim/battle-stream.mjs +2 -3
  130. package/build/esm/sim/battle-stream.mjs.map +1 -1
  131. package/build/esm/sim/battle.d.mts +13 -7
  132. package/build/esm/sim/battle.mjs +184 -59
  133. package/build/esm/sim/battle.mjs.map +1 -1
  134. package/build/esm/sim/dex-abilities.d.mts +0 -1
  135. package/build/esm/sim/dex-abilities.mjs +1 -1
  136. package/build/esm/sim/dex-abilities.mjs.map +1 -1
  137. package/build/esm/sim/dex-conditions.d.mts +11 -3
  138. package/build/esm/sim/dex-conditions.mjs +1 -1
  139. package/build/esm/sim/dex-conditions.mjs.map +1 -1
  140. package/build/esm/sim/dex-data.mjs +1 -1
  141. package/build/esm/sim/dex-data.mjs.map +1 -1
  142. package/build/esm/sim/dex-formats.d.mts +2 -1
  143. package/build/esm/sim/dex-formats.mjs.map +1 -1
  144. package/build/esm/sim/dex-items.d.mts +3 -1
  145. package/build/esm/sim/dex-items.mjs +2 -1
  146. package/build/esm/sim/dex-items.mjs.map +1 -1
  147. package/build/esm/sim/dex-moves.mjs +1 -1
  148. package/build/esm/sim/dex-moves.mjs.map +1 -1
  149. package/build/esm/sim/exported-global-types.d.mts +6 -0
  150. package/build/esm/sim/field.mjs +9 -9
  151. package/build/esm/sim/field.mjs.map +1 -1
  152. package/build/esm/sim/global-types.d.mts +6 -0
  153. package/build/esm/sim/pokemon.d.mts +8 -4
  154. package/build/esm/sim/pokemon.mjs +61 -46
  155. package/build/esm/sim/pokemon.mjs.map +1 -1
  156. package/build/esm/sim/prng.d.mts +7 -4
  157. package/build/esm/sim/prng.mjs +34 -16
  158. package/build/esm/sim/prng.mjs.map +1 -1
  159. package/build/esm/sim/side.mjs +8 -5
  160. package/build/esm/sim/side.mjs.map +1 -1
  161. package/build/esm/sim/team-validator.mjs +10 -11
  162. package/build/esm/sim/team-validator.mjs.map +1 -1
  163. package/build/esm/sim/tools/exhaustive-runner.mjs +3 -4
  164. package/build/esm/sim/tools/exhaustive-runner.mjs.map +1 -1
  165. package/build/esm/sim/tools/random-player-ai.mjs +1 -1
  166. package/build/esm/sim/tools/random-player-ai.mjs.map +1 -1
  167. package/build/esm/sim/tools/runner.mjs +2 -3
  168. package/build/esm/sim/tools/runner.mjs.map +1 -1
  169. package/package.json +2 -2
@@ -22,7 +22,7 @@ import { Side } from './side.mjs';
22
22
  import { State } from './state.mjs';
23
23
  import { BattleQueue } from './battle-queue.mjs';
24
24
  import { BattleActions } from './battle-actions.mjs';
25
- import { Utils } from '../lib/index.mjs';
25
+ import { Utils } from '../lib/utils.mjs';
26
26
  const splitRegex = /^\|split\|p([1234])\n(.*)\n(.*)|.+/gm;
27
27
  export function extractChannelMessages(message, channelIds) {
28
28
  const channelIdSet = new Set(channelIds);
@@ -75,7 +75,7 @@ export class Battle {
75
75
  options.forceRandomChance : null;
76
76
  this.deserialized = !!options.deserialized;
77
77
  this.strictChoices = !!options.strictChoices;
78
- this.formatData = { id: format.id };
78
+ this.formatData = this.initEffectState({ id: format.id });
79
79
  this.gameType = (format.gameType || 'singles');
80
80
  this.field = new Field(this);
81
81
  this.sides = Array(format.playerCount).fill(null);
@@ -101,7 +101,7 @@ export class Battle {
101
101
  this.started = false;
102
102
  this.ended = false;
103
103
  this.effect = { id: '' };
104
- this.effectState = { id: '' };
104
+ this.effectState = this.initEffectState({ id: '' });
105
105
  this.event = { id: '' };
106
106
  this.events = null;
107
107
  this.eventDepth = 0;
@@ -112,8 +112,12 @@ export class Battle {
112
112
  this.lastMoveLine = -1;
113
113
  this.lastSuccessfulMoveThisTurn = null;
114
114
  this.lastDamage = 0;
115
- this.abilityOrder = 0;
115
+ this.effectOrder = 0;
116
116
  this.quickClawRoll = false;
117
+ this.speedOrder = [];
118
+ for (let i = 0; i < this.activePerHalf * 2; i++) {
119
+ this.speedOrder.push(i);
120
+ }
117
121
  this.teamGenerator = null;
118
122
  this.hints = new Set();
119
123
  this.NOT_FAIL = '';
@@ -226,6 +230,7 @@ export class Battle {
226
230
  * 2. Priority, high to low (default 0)
227
231
  * 3. Speed, high to low (default 0)
228
232
  * 4. SubOrder, low to high (default 0)
233
+ * 5. EffectOrder, low to high (default 0)
229
234
  *
230
235
  * Doesn't reference `this` so doesn't need to be bound.
231
236
  */
@@ -234,12 +239,14 @@ export class Battle {
234
239
  ((b.priority || 0) - (a.priority || 0)) ||
235
240
  ((b.speed || 0) - (a.speed || 0)) ||
236
241
  -((b.subOrder || 0) - (a.subOrder || 0)) ||
242
+ -((b.effectOrder || 0) - (a.effectOrder || 0)) ||
237
243
  0;
238
244
  }
239
245
  static compareRedirectOrder(a, b) {
240
246
  return ((b.priority || 0) - (a.priority || 0)) ||
241
247
  ((b.speed || 0) - (a.speed || 0)) ||
242
- ((a.effectHolder && b.effectHolder) ? -(b.effectHolder.abilityOrder - a.effectHolder.abilityOrder) : 0) ||
248
+ ((a.effectHolder?.abilityState && b.effectHolder?.abilityState) ?
249
+ -(b.effectHolder.abilityState.effectOrder - a.effectHolder.abilityState.effectOrder) : 0) ||
243
250
  0;
244
251
  }
245
252
  static compareLeftToRightOrder(a, b) {
@@ -304,22 +311,32 @@ export class Battle {
304
311
  /**
305
312
  * Runs an event with no source on each effect on the field, in Speed order.
306
313
  *
307
- * Unlike `eachEvent`, this contains a lot of other handling and is intended only for the residual step.
314
+ * Unlike `eachEvent`, this contains a lot of other handling and is only intended for
315
+ * the 'Residual' and 'SwitchIn' events.
308
316
  */
309
- residualEvent(eventid, relayVar) {
317
+ fieldEvent(eventid, targets) {
310
318
  const callbackName = `on${eventid}`;
311
- let handlers = this.findBattleEventHandlers(callbackName, 'duration');
312
- handlers = handlers.concat(this.findFieldEventHandlers(this.field, `onField${eventid}`, 'duration'));
319
+ let getKey;
320
+ if (eventid === 'Residual') {
321
+ getKey = 'duration';
322
+ }
323
+ let handlers = this.findFieldEventHandlers(this.field, `onField${eventid}`, getKey);
313
324
  for (const side of this.sides) {
314
325
  if (side.n < 2 || !side.allySide) {
315
- handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, 'duration'));
326
+ handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, getKey));
316
327
  }
317
328
  for (const active of side.active) {
318
329
  if (!active)
319
330
  continue;
320
- handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, 'duration'));
331
+ if (eventid === 'SwitchIn') {
332
+ handlers = handlers.concat(this.findPokemonEventHandlers(active, `onAny${eventid}`));
333
+ }
334
+ if (targets && !targets.includes(active))
335
+ continue;
336
+ handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, getKey));
321
337
  handlers = handlers.concat(this.findSideEventHandlers(side, callbackName, undefined, active));
322
338
  handlers = handlers.concat(this.findFieldEventHandlers(this.field, callbackName, undefined, active));
339
+ handlers = handlers.concat(this.findBattleEventHandlers(callbackName, getKey, active));
323
340
  }
324
341
  }
325
342
  this.speedSort(handlers);
@@ -327,9 +344,11 @@ export class Battle {
327
344
  const handler = handlers[0];
328
345
  handlers.shift();
329
346
  const effect = handler.effect;
330
- if (handler.effectHolder.fainted)
331
- continue;
332
- if (handler.end && handler.state && handler.state.duration) {
347
+ if (handler.effectHolder.fainted) {
348
+ if (!(handler.state?.isSlotCondition))
349
+ continue;
350
+ }
351
+ if (eventid === 'Residual' && handler.end && handler.state && handler.state.duration) {
333
352
  handler.state.duration--;
334
353
  if (!handler.state.duration) {
335
354
  const endCallArgs = handler.endCallArgs || [handler.effectHolder, effect.id];
@@ -345,7 +364,7 @@ export class Battle {
345
364
  if (handler.effectHolder.pseudoWeather)
346
365
  handlerEventid = `Field${eventid}`;
347
366
  if (handler.callback) {
348
- this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, relayVar, handler.callback);
367
+ this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, undefined, handler.callback);
349
368
  }
350
369
  this.faintMessages();
351
370
  if (this.ended)
@@ -379,8 +398,8 @@ export class Battle {
379
398
  // it's changed; call it off
380
399
  return relayVar;
381
400
  }
382
- if (eventid !== 'Start' && eventid !== 'TakeItem' && eventid !== 'Primal' &&
383
- effect.effectType === 'Item' && (target instanceof Pokemon) && target.ignoringItem()) {
401
+ if (eventid !== 'Start' && eventid !== 'TakeItem' && effect.effectType === 'Item' &&
402
+ (target instanceof Pokemon) && target.ignoringItem()) {
384
403
  this.debug(eventid + ' handler suppressed by Embargo, Klutz or Magic Room');
385
404
  return relayVar;
386
405
  }
@@ -400,7 +419,7 @@ export class Battle {
400
419
  const parentEffectState = this.effectState;
401
420
  const parentEvent = this.event;
402
421
  this.effect = effect;
403
- this.effectState = state || {};
422
+ this.effectState = state || this.initEffectState({});
404
423
  this.event = { id: eventid, target, source, effect: sourceEffect };
405
424
  this.eventDepth++;
406
425
  const args = [target, source, sourceEffect];
@@ -551,7 +570,7 @@ export class Battle {
551
570
  if (Array.isArray(target))
552
571
  throw new Error("");
553
572
  handlers.unshift(this.resolvePriority({
554
- effect: sourceEffect, callback, state: {}, end: null, effectHolder: target,
573
+ effect: sourceEffect, callback, state: this.initEffectState({}), end: null, effectHolder: target,
555
574
  }, `on${eventid}`));
556
575
  }
557
576
  }
@@ -670,7 +689,7 @@ export class Battle {
670
689
  const parentEffect = this.effect;
671
690
  const parentEffectState = this.effectState;
672
691
  this.effect = handler.effect;
673
- this.effectState = handler.state || {};
692
+ this.effectState = handler.state || this.initEffectState({});
674
693
  this.effectState.target = effectHolder;
675
694
  returnVal = handler.callback.apply(this, args);
676
695
  this.effect = parentEffect;
@@ -712,15 +731,74 @@ export class Battle {
712
731
  priorityEvent(eventid, target, source, effect, relayVar, onEffect) {
713
732
  return this.runEvent(eventid, target, source, effect, relayVar, onEffect, true);
714
733
  }
715
- resolvePriority(handler, callbackName) {
734
+ resolvePriority(h, callbackName) {
735
+ const handler = h;
716
736
  // @ts-ignore
717
737
  handler.order = handler.effect[`${callbackName}Order`] || false;
718
738
  // @ts-ignore
719
739
  handler.priority = handler.effect[`${callbackName}Priority`] || 0;
720
740
  // @ts-ignore
721
741
  handler.subOrder = handler.effect[`${callbackName}SubOrder`] || 0;
742
+ if (!handler.subOrder) {
743
+ // https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-59#post-8685465
744
+ const effectTypeOrder = {
745
+ // Z-Move: 1,
746
+ Condition: 2,
747
+ // Slot Condition: 3,
748
+ // Side Condition: 4,
749
+ // Field Condition: 5, (includes weather but also terrains and pseudoweathers)
750
+ Weather: 5,
751
+ Format: 5,
752
+ Rule: 5,
753
+ Ruleset: 5,
754
+ // Poison Touch: 6, (also includes Perish Body)
755
+ Ability: 7,
756
+ Item: 8,
757
+ // Stall: 9,
758
+ };
759
+ handler.subOrder = effectTypeOrder[handler.effect.effectType] || 0;
760
+ if (handler.effect.effectType === 'Condition') {
761
+ if (handler.state?.target instanceof Side) {
762
+ if (handler.state.isSlotCondition) {
763
+ // slot condition
764
+ handler.subOrder = 3;
765
+ }
766
+ else {
767
+ // side condition
768
+ handler.subOrder = 4;
769
+ }
770
+ }
771
+ else if (handler.state?.target instanceof Field) {
772
+ // field condition
773
+ handler.subOrder = 5;
774
+ }
775
+ }
776
+ else if (handler.effect.effectType === 'Ability') {
777
+ if (handler.effect.name === 'Poison Touch' || handler.effect.name === 'Perish Body') {
778
+ handler.subOrder = 6;
779
+ }
780
+ else if (handler.effect.name === 'Stall') {
781
+ handler.subOrder = 9;
782
+ }
783
+ }
784
+ }
785
+ if (callbackName.endsWith('SwitchIn') || callbackName.endsWith('RedirectTarget')) {
786
+ // If multiple hazards are present on one side, their event handlers all perfectly tie in speed, priority,
787
+ // and subOrder. They should activate in the order they were created, which is where effectOrder comes in.
788
+ // This also applies to speed ties for which ability like Lightning Rod redirects moves.
789
+ // TODO: In-game, other events are also sorted this way, but that's an implementation for another refactor
790
+ handler.effectOrder = handler.state?.effectOrder;
791
+ }
722
792
  if (handler.effectHolder && handler.effectHolder.getStat) {
723
- handler.speed = handler.effectHolder.speed;
793
+ const pokemon = handler.effectHolder;
794
+ handler.speed = pokemon.speed;
795
+ if (callbackName.endsWith('SwitchIn')) {
796
+ // Pokemon speeds including ties are resolved before all onSwitchIn handlers and aren't re-sorted in-between
797
+ // so we subtract a fractional speed from each Pokemon's respective event handlers by using the index of their
798
+ // unique field position in a pre-sorted-by-speed array
799
+ const fieldPositionValue = pokemon.side.n * this.sides.length + pokemon.position;
800
+ handler.speed -= this.speedOrder.indexOf(fieldPositionValue) / (this.activePerHalf * 2);
801
+ }
724
802
  }
725
803
  return handler;
726
804
  }
@@ -795,7 +873,22 @@ export class Battle {
795
873
  effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
796
874
  }, callbackName));
797
875
  }
876
+ else if (['ability', 'item'].includes(volatile.id.split(':')[0])) {
877
+ // Innate abilities/items; see comment below
878
+ // @ts-ignore - dynamic lookup
879
+ if (this.gen >= 5 && callbackName === 'onSwitchIn' && !volatile.onAnySwitchIn) {
880
+ callback = volatile.onStart;
881
+ if (callback !== undefined || (getKey && volatileState[getKey])) {
882
+ handlers.push(this.resolvePriority({
883
+ effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
884
+ }, callbackName));
885
+ }
886
+ }
887
+ }
798
888
  }
889
+ // Abilities and items Start at different times during the SwitchIn event, so we run their onStart handlers
890
+ // during the SwitchIn event instead of running the Start event during switch-ins
891
+ // gens 4 and before still use the old system, though
799
892
  const ability = pokemon.getAbility();
800
893
  // @ts-ignore - dynamic lookup
801
894
  callback = ability[callbackName];
@@ -803,6 +896,16 @@ export class Battle {
803
896
  handlers.push(this.resolvePriority({
804
897
  effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
805
898
  }, callbackName));
899
+ // @ts-ignore - dynamic lookup
900
+ }
901
+ else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !ability.onAnySwitchIn) {
902
+ // @ts-ignore - dynamic lookup
903
+ callback = ability.onStart;
904
+ if (callback !== undefined || (getKey && pokemon.abilityState[getKey])) {
905
+ handlers.push(this.resolvePriority({
906
+ effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
907
+ }, callbackName));
908
+ }
806
909
  }
807
910
  const item = pokemon.getItem();
808
911
  // @ts-ignore - dynamic lookup
@@ -811,6 +914,16 @@ export class Battle {
811
914
  handlers.push(this.resolvePriority({
812
915
  effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
813
916
  }, callbackName));
917
+ // @ts-ignore - dynamic lookup
918
+ }
919
+ else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !item.onAnySwitchIn) {
920
+ // @ts-ignore - dynamic lookup
921
+ callback = item.onStart;
922
+ if (callback !== undefined || (getKey && pokemon.itemState[getKey])) {
923
+ handlers.push(this.resolvePriority({
924
+ effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
925
+ }, callbackName));
926
+ }
814
927
  }
815
928
  const species = pokemon.baseSpecies;
816
929
  // @ts-ignore - dynamic lookup
@@ -833,13 +946,13 @@ export class Battle {
833
946
  state: slotConditionState,
834
947
  end: side.removeSlotCondition,
835
948
  endCallArgs: [side, pokemon, slotCondition.id],
836
- effectHolder: side,
949
+ effectHolder: pokemon,
837
950
  }, callbackName));
838
951
  }
839
952
  }
840
953
  return handlers;
841
954
  }
842
- findBattleEventHandlers(callbackName, getKey) {
955
+ findBattleEventHandlers(callbackName, getKey, customHolder) {
843
956
  const handlers = [];
844
957
  let callback;
845
958
  const format = this.format;
@@ -847,15 +960,15 @@ export class Battle {
847
960
  callback = format[callbackName];
848
961
  if (callback !== undefined || (getKey && this.formatData[getKey])) {
849
962
  handlers.push(this.resolvePriority({
850
- effect: format, callback, state: this.formatData, end: null, effectHolder: this,
963
+ effect: format, callback, state: this.formatData, end: null, effectHolder: customHolder || this,
851
964
  }, callbackName));
852
965
  }
853
966
  if (this.events && (callback = this.events[callbackName]) !== undefined) {
854
967
  for (const handler of callback) {
855
968
  const state = (handler.target.effectType === 'Format') ? this.formatData : null;
856
969
  handlers.push({
857
- effect: handler.target, callback: handler.callback, state, end: null,
858
- effectHolder: this, priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
970
+ effect: handler.target, callback: handler.callback, state, end: null, effectHolder: customHolder || this,
971
+ priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
859
972
  });
860
973
  }
861
974
  }
@@ -998,11 +1111,11 @@ export class Battle {
998
1111
  }
999
1112
  return pokemonList;
1000
1113
  }
1001
- getAllActive() {
1114
+ getAllActive(includeFainted) {
1002
1115
  const pokemonList = [];
1003
1116
  for (const side of this.sides) {
1004
1117
  for (const pokemon of side.active) {
1005
- if (pokemon && !pokemon.fainted) {
1118
+ if (pokemon && (includeFainted || !pokemon.fainted)) {
1006
1119
  pokemonList.push(pokemon);
1007
1120
  }
1008
1121
  }
@@ -1397,24 +1510,6 @@ export class Battle {
1397
1510
  this.quickClawRoll = this.randomChance(60, 256);
1398
1511
  if (this.gen === 3)
1399
1512
  this.quickClawRoll = this.randomChance(1, 5);
1400
- // Crazyhouse Progress checker because sidebars has trouble keeping track of Pokemon.
1401
- // Please remove me once there is client support.
1402
- if (this.ruleTable.has('crazyhouserule')) {
1403
- for (const side of this.sides) {
1404
- let buf = `raw|${Utils.escapeHTML(side.name)}'s team:<br />`;
1405
- for (const pokemon of side.pokemon) {
1406
- if (!buf.endsWith('<br />'))
1407
- buf += '/</span>&#8203;';
1408
- if (pokemon.fainted) {
1409
- buf += `<span style="white-space:nowrap;"><span style="opacity:.3"><psicon pokemon="${pokemon.species.id}" /></span>`;
1410
- }
1411
- else {
1412
- buf += `<span style="white-space:nowrap"><psicon pokemon="${pokemon.species.id}" />`;
1413
- }
1414
- }
1415
- this.add(`${buf}</span>`);
1416
- }
1417
- }
1418
1513
  this.makeRequest('move');
1419
1514
  }
1420
1515
  maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide) {
@@ -2145,12 +2240,23 @@ export class Battle {
2145
2240
  pokemon.side.totalFainted++;
2146
2241
  this.runEvent('Faint', pokemon, faintData.source, faintData.effect);
2147
2242
  this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon);
2243
+ this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
2244
+ if (pokemon.regressionForme) {
2245
+ // before clearing volatiles
2246
+ pokemon.baseSpecies = this.dex.species.get(pokemon.set.species || pokemon.set.name);
2247
+ pokemon.baseAbility = toID(pokemon.set.ability);
2248
+ }
2148
2249
  pokemon.clearVolatile(false);
2149
2250
  pokemon.fainted = true;
2150
2251
  pokemon.illusion = null;
2151
2252
  pokemon.isActive = false;
2152
2253
  pokemon.isStarted = false;
2153
2254
  delete pokemon.terastallized;
2255
+ if (pokemon.regressionForme) {
2256
+ // after clearing volatiles
2257
+ pokemon.details = pokemon.getUpdatedDetails();
2258
+ pokemon.regressionForme = false;
2259
+ }
2154
2260
  pokemon.side.faintedThisTurn = pokemon;
2155
2261
  if (this.faintQueue.length >= faintQueueLeft)
2156
2262
  checkWin = true;
@@ -2270,8 +2376,7 @@ export class Battle {
2270
2376
  if (!species)
2271
2377
  continue;
2272
2378
  pokemon.baseSpecies = rawSpecies;
2273
- pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
2274
- (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
2379
+ pokemon.details = pokemon.getUpdatedDetails();
2275
2380
  pokemon.setAbility(species.abilities['0'], null, true);
2276
2381
  pokemon.baseAbility = pokemon.ability;
2277
2382
  const behemothMove = {
@@ -2424,17 +2529,9 @@ export class Battle {
2424
2529
  this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
2425
2530
  action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
2426
2531
  break;
2427
- case 'runUnnerve':
2428
- this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
2429
- break;
2430
2532
  case 'runSwitch':
2431
2533
  this.actions.runSwitch(action.pokemon);
2432
2534
  break;
2433
- case 'runPrimal':
2434
- if (!action.pokemon.transformed) {
2435
- this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
2436
- }
2437
- break;
2438
2535
  case 'shift':
2439
2536
  if (!action.pokemon.isActive)
2440
2537
  return false;
@@ -2450,7 +2547,7 @@ export class Battle {
2450
2547
  this.clearActiveMove(true);
2451
2548
  this.updateSpeed();
2452
2549
  residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()]);
2453
- this.residualEvent('Residual');
2550
+ this.fieldEvent('Residual');
2454
2551
  this.add('upkeep');
2455
2552
  break;
2456
2553
  }
@@ -2491,7 +2588,7 @@ export class Battle {
2491
2588
  else if (this.queue.peek()?.choice === 'instaswitch') {
2492
2589
  return false;
2493
2590
  }
2494
- if (this.gen >= 5) {
2591
+ if (this.gen >= 5 && action.choice !== 'start') {
2495
2592
  this.eachEvent('Update');
2496
2593
  for (const [pokemon, originalHP] of residualPokemon) {
2497
2594
  const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
@@ -2916,6 +3013,34 @@ export class Battle {
2916
3013
  getOverflowedTurnCount() {
2917
3014
  return this.gen >= 8 ? (this.turn - 1) % 256 : this.turn - 1;
2918
3015
  }
3016
+ initEffectState(obj, effectOrder) {
3017
+ if (!obj.id)
3018
+ obj.id = '';
3019
+ if (effectOrder !== undefined) {
3020
+ obj.effectOrder = effectOrder;
3021
+ }
3022
+ else if (obj.id && obj.target && (!(obj.target instanceof Pokemon) || obj.target.isActive)) {
3023
+ obj.effectOrder = this.effectOrder++;
3024
+ }
3025
+ else {
3026
+ obj.effectOrder = 0;
3027
+ }
3028
+ return obj;
3029
+ }
3030
+ clearEffectState(state) {
3031
+ state.id = '';
3032
+ for (const k in state) {
3033
+ if (k === 'id' || k === 'target') {
3034
+ continue;
3035
+ }
3036
+ else if (k === 'effectOrder') {
3037
+ state.effectOrder = 0;
3038
+ }
3039
+ else {
3040
+ delete state[k];
3041
+ }
3042
+ }
3043
+ }
2919
3044
  destroy() {
2920
3045
  // deallocate ourself
2921
3046
  // deallocate children and get rid of references to them