@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
@@ -26,7 +26,7 @@ const side_1 = require("./side");
26
26
  const state_1 = require("./state");
27
27
  const battle_queue_1 = require("./battle-queue");
28
28
  const battle_actions_1 = require("./battle-actions");
29
- const lib_1 = require("../lib");
29
+ const utils_1 = require("../lib/utils");
30
30
  const splitRegex = /^\|split\|p([1234])\n(.*)\n(.*)|.+/gm;
31
31
  function extractChannelMessages(message, channelIds) {
32
32
  const channelIdSet = new Set(channelIds);
@@ -63,7 +63,7 @@ class Battle {
63
63
  this.gen = this.dex.gen;
64
64
  this.ruleTable = this.dex.formats.getRuleTable(format);
65
65
  this.trunc = this.dex.trunc;
66
- this.clampIntRange = lib_1.Utils.clampIntRange;
66
+ this.clampIntRange = utils_1.Utils.clampIntRange;
67
67
  // Object.assign(this, this.dex.data.Scripts);
68
68
  for (const i in this.dex.data.Scripts) {
69
69
  const entry = this.dex.data.Scripts[i];
@@ -79,7 +79,7 @@ class Battle {
79
79
  options.forceRandomChance : null;
80
80
  this.deserialized = !!options.deserialized;
81
81
  this.strictChoices = !!options.strictChoices;
82
- this.formatData = { id: format.id };
82
+ this.formatData = this.initEffectState({ id: format.id });
83
83
  this.gameType = (format.gameType || 'singles');
84
84
  this.field = new field_1.Field(this);
85
85
  this.sides = Array(format.playerCount).fill(null);
@@ -105,7 +105,7 @@ class Battle {
105
105
  this.started = false;
106
106
  this.ended = false;
107
107
  this.effect = { id: '' };
108
- this.effectState = { id: '' };
108
+ this.effectState = this.initEffectState({ id: '' });
109
109
  this.event = { id: '' };
110
110
  this.events = null;
111
111
  this.eventDepth = 0;
@@ -116,8 +116,12 @@ class Battle {
116
116
  this.lastMoveLine = -1;
117
117
  this.lastSuccessfulMoveThisTurn = null;
118
118
  this.lastDamage = 0;
119
- this.abilityOrder = 0;
119
+ this.effectOrder = 0;
120
120
  this.quickClawRoll = false;
121
+ this.speedOrder = [];
122
+ for (let i = 0; i < this.activePerHalf * 2; i++) {
123
+ this.speedOrder.push(i);
124
+ }
121
125
  this.teamGenerator = null;
122
126
  this.hints = new Set();
123
127
  this.NOT_FAIL = '';
@@ -230,6 +234,7 @@ class Battle {
230
234
  * 2. Priority, high to low (default 0)
231
235
  * 3. Speed, high to low (default 0)
232
236
  * 4. SubOrder, low to high (default 0)
237
+ * 5. EffectOrder, low to high (default 0)
233
238
  *
234
239
  * Doesn't reference `this` so doesn't need to be bound.
235
240
  */
@@ -238,12 +243,14 @@ class Battle {
238
243
  ((b.priority || 0) - (a.priority || 0)) ||
239
244
  ((b.speed || 0) - (a.speed || 0)) ||
240
245
  -((b.subOrder || 0) - (a.subOrder || 0)) ||
246
+ -((b.effectOrder || 0) - (a.effectOrder || 0)) ||
241
247
  0;
242
248
  }
243
249
  static compareRedirectOrder(a, b) {
244
250
  return ((b.priority || 0) - (a.priority || 0)) ||
245
251
  ((b.speed || 0) - (a.speed || 0)) ||
246
- ((a.effectHolder && b.effectHolder) ? -(b.effectHolder.abilityOrder - a.effectHolder.abilityOrder) : 0) ||
252
+ ((a.effectHolder?.abilityState && b.effectHolder?.abilityState) ?
253
+ -(b.effectHolder.abilityState.effectOrder - a.effectHolder.abilityState.effectOrder) : 0) ||
247
254
  0;
248
255
  }
249
256
  static compareLeftToRightOrder(a, b) {
@@ -308,22 +315,32 @@ class Battle {
308
315
  /**
309
316
  * Runs an event with no source on each effect on the field, in Speed order.
310
317
  *
311
- * Unlike `eachEvent`, this contains a lot of other handling and is intended only for the residual step.
318
+ * Unlike `eachEvent`, this contains a lot of other handling and is only intended for
319
+ * the 'Residual' and 'SwitchIn' events.
312
320
  */
313
- residualEvent(eventid, relayVar) {
321
+ fieldEvent(eventid, targets) {
314
322
  const callbackName = `on${eventid}`;
315
- let handlers = this.findBattleEventHandlers(callbackName, 'duration');
316
- handlers = handlers.concat(this.findFieldEventHandlers(this.field, `onField${eventid}`, 'duration'));
323
+ let getKey;
324
+ if (eventid === 'Residual') {
325
+ getKey = 'duration';
326
+ }
327
+ let handlers = this.findFieldEventHandlers(this.field, `onField${eventid}`, getKey);
317
328
  for (const side of this.sides) {
318
329
  if (side.n < 2 || !side.allySide) {
319
- handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, 'duration'));
330
+ handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, getKey));
320
331
  }
321
332
  for (const active of side.active) {
322
333
  if (!active)
323
334
  continue;
324
- handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, 'duration'));
335
+ if (eventid === 'SwitchIn') {
336
+ handlers = handlers.concat(this.findPokemonEventHandlers(active, `onAny${eventid}`));
337
+ }
338
+ if (targets && !targets.includes(active))
339
+ continue;
340
+ handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, getKey));
325
341
  handlers = handlers.concat(this.findSideEventHandlers(side, callbackName, undefined, active));
326
342
  handlers = handlers.concat(this.findFieldEventHandlers(this.field, callbackName, undefined, active));
343
+ handlers = handlers.concat(this.findBattleEventHandlers(callbackName, getKey, active));
327
344
  }
328
345
  }
329
346
  this.speedSort(handlers);
@@ -331,9 +348,11 @@ class Battle {
331
348
  const handler = handlers[0];
332
349
  handlers.shift();
333
350
  const effect = handler.effect;
334
- if (handler.effectHolder.fainted)
335
- continue;
336
- if (handler.end && handler.state && handler.state.duration) {
351
+ if (handler.effectHolder.fainted) {
352
+ if (!(handler.state?.isSlotCondition))
353
+ continue;
354
+ }
355
+ if (eventid === 'Residual' && handler.end && handler.state && handler.state.duration) {
337
356
  handler.state.duration--;
338
357
  if (!handler.state.duration) {
339
358
  const endCallArgs = handler.endCallArgs || [handler.effectHolder, effect.id];
@@ -349,7 +368,7 @@ class Battle {
349
368
  if (handler.effectHolder.pseudoWeather)
350
369
  handlerEventid = `Field${eventid}`;
351
370
  if (handler.callback) {
352
- this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, relayVar, handler.callback);
371
+ this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, undefined, handler.callback);
353
372
  }
354
373
  this.faintMessages();
355
374
  if (this.ended)
@@ -383,8 +402,8 @@ class Battle {
383
402
  // it's changed; call it off
384
403
  return relayVar;
385
404
  }
386
- if (eventid !== 'Start' && eventid !== 'TakeItem' && eventid !== 'Primal' &&
387
- effect.effectType === 'Item' && (target instanceof pokemon_1.Pokemon) && target.ignoringItem()) {
405
+ if (eventid !== 'Start' && eventid !== 'TakeItem' && effect.effectType === 'Item' &&
406
+ (target instanceof pokemon_1.Pokemon) && target.ignoringItem()) {
388
407
  this.debug(eventid + ' handler suppressed by Embargo, Klutz or Magic Room');
389
408
  return relayVar;
390
409
  }
@@ -404,7 +423,7 @@ class Battle {
404
423
  const parentEffectState = this.effectState;
405
424
  const parentEvent = this.event;
406
425
  this.effect = effect;
407
- this.effectState = state || {};
426
+ this.effectState = state || this.initEffectState({});
408
427
  this.event = { id: eventid, target, source, effect: sourceEffect };
409
428
  this.eventDepth++;
410
429
  const args = [target, source, sourceEffect];
@@ -555,7 +574,7 @@ class Battle {
555
574
  if (Array.isArray(target))
556
575
  throw new Error("");
557
576
  handlers.unshift(this.resolvePriority({
558
- effect: sourceEffect, callback, state: {}, end: null, effectHolder: target,
577
+ effect: sourceEffect, callback, state: this.initEffectState({}), end: null, effectHolder: target,
559
578
  }, `on${eventid}`));
560
579
  }
561
580
  }
@@ -674,7 +693,7 @@ class Battle {
674
693
  const parentEffect = this.effect;
675
694
  const parentEffectState = this.effectState;
676
695
  this.effect = handler.effect;
677
- this.effectState = handler.state || {};
696
+ this.effectState = handler.state || this.initEffectState({});
678
697
  this.effectState.target = effectHolder;
679
698
  returnVal = handler.callback.apply(this, args);
680
699
  this.effect = parentEffect;
@@ -716,15 +735,74 @@ class Battle {
716
735
  priorityEvent(eventid, target, source, effect, relayVar, onEffect) {
717
736
  return this.runEvent(eventid, target, source, effect, relayVar, onEffect, true);
718
737
  }
719
- resolvePriority(handler, callbackName) {
738
+ resolvePriority(h, callbackName) {
739
+ const handler = h;
720
740
  // @ts-ignore
721
741
  handler.order = handler.effect[`${callbackName}Order`] || false;
722
742
  // @ts-ignore
723
743
  handler.priority = handler.effect[`${callbackName}Priority`] || 0;
724
744
  // @ts-ignore
725
745
  handler.subOrder = handler.effect[`${callbackName}SubOrder`] || 0;
746
+ if (!handler.subOrder) {
747
+ // https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-59#post-8685465
748
+ const effectTypeOrder = {
749
+ // Z-Move: 1,
750
+ Condition: 2,
751
+ // Slot Condition: 3,
752
+ // Side Condition: 4,
753
+ // Field Condition: 5, (includes weather but also terrains and pseudoweathers)
754
+ Weather: 5,
755
+ Format: 5,
756
+ Rule: 5,
757
+ Ruleset: 5,
758
+ // Poison Touch: 6, (also includes Perish Body)
759
+ Ability: 7,
760
+ Item: 8,
761
+ // Stall: 9,
762
+ };
763
+ handler.subOrder = effectTypeOrder[handler.effect.effectType] || 0;
764
+ if (handler.effect.effectType === 'Condition') {
765
+ if (handler.state?.target instanceof side_1.Side) {
766
+ if (handler.state.isSlotCondition) {
767
+ // slot condition
768
+ handler.subOrder = 3;
769
+ }
770
+ else {
771
+ // side condition
772
+ handler.subOrder = 4;
773
+ }
774
+ }
775
+ else if (handler.state?.target instanceof field_1.Field) {
776
+ // field condition
777
+ handler.subOrder = 5;
778
+ }
779
+ }
780
+ else if (handler.effect.effectType === 'Ability') {
781
+ if (handler.effect.name === 'Poison Touch' || handler.effect.name === 'Perish Body') {
782
+ handler.subOrder = 6;
783
+ }
784
+ else if (handler.effect.name === 'Stall') {
785
+ handler.subOrder = 9;
786
+ }
787
+ }
788
+ }
789
+ if (callbackName.endsWith('SwitchIn') || callbackName.endsWith('RedirectTarget')) {
790
+ // If multiple hazards are present on one side, their event handlers all perfectly tie in speed, priority,
791
+ // and subOrder. They should activate in the order they were created, which is where effectOrder comes in.
792
+ // This also applies to speed ties for which ability like Lightning Rod redirects moves.
793
+ // TODO: In-game, other events are also sorted this way, but that's an implementation for another refactor
794
+ handler.effectOrder = handler.state?.effectOrder;
795
+ }
726
796
  if (handler.effectHolder && handler.effectHolder.getStat) {
727
- handler.speed = handler.effectHolder.speed;
797
+ const pokemon = handler.effectHolder;
798
+ handler.speed = pokemon.speed;
799
+ if (callbackName.endsWith('SwitchIn')) {
800
+ // Pokemon speeds including ties are resolved before all onSwitchIn handlers and aren't re-sorted in-between
801
+ // so we subtract a fractional speed from each Pokemon's respective event handlers by using the index of their
802
+ // unique field position in a pre-sorted-by-speed array
803
+ const fieldPositionValue = pokemon.side.n * this.sides.length + pokemon.position;
804
+ handler.speed -= this.speedOrder.indexOf(fieldPositionValue) / (this.activePerHalf * 2);
805
+ }
728
806
  }
729
807
  return handler;
730
808
  }
@@ -799,7 +877,22 @@ class Battle {
799
877
  effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
800
878
  }, callbackName));
801
879
  }
880
+ else if (['ability', 'item'].includes(volatile.id.split(':')[0])) {
881
+ // Innate abilities/items; see comment below
882
+ // @ts-ignore - dynamic lookup
883
+ if (this.gen >= 5 && callbackName === 'onSwitchIn' && !volatile.onAnySwitchIn) {
884
+ callback = volatile.onStart;
885
+ if (callback !== undefined || (getKey && volatileState[getKey])) {
886
+ handlers.push(this.resolvePriority({
887
+ effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
888
+ }, callbackName));
889
+ }
890
+ }
891
+ }
802
892
  }
893
+ // Abilities and items Start at different times during the SwitchIn event, so we run their onStart handlers
894
+ // during the SwitchIn event instead of running the Start event during switch-ins
895
+ // gens 4 and before still use the old system, though
803
896
  const ability = pokemon.getAbility();
804
897
  // @ts-ignore - dynamic lookup
805
898
  callback = ability[callbackName];
@@ -807,6 +900,16 @@ class Battle {
807
900
  handlers.push(this.resolvePriority({
808
901
  effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
809
902
  }, callbackName));
903
+ // @ts-ignore - dynamic lookup
904
+ }
905
+ else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !ability.onAnySwitchIn) {
906
+ // @ts-ignore - dynamic lookup
907
+ callback = ability.onStart;
908
+ if (callback !== undefined || (getKey && pokemon.abilityState[getKey])) {
909
+ handlers.push(this.resolvePriority({
910
+ effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
911
+ }, callbackName));
912
+ }
810
913
  }
811
914
  const item = pokemon.getItem();
812
915
  // @ts-ignore - dynamic lookup
@@ -815,6 +918,16 @@ class Battle {
815
918
  handlers.push(this.resolvePriority({
816
919
  effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
817
920
  }, callbackName));
921
+ // @ts-ignore - dynamic lookup
922
+ }
923
+ else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !item.onAnySwitchIn) {
924
+ // @ts-ignore - dynamic lookup
925
+ callback = item.onStart;
926
+ if (callback !== undefined || (getKey && pokemon.itemState[getKey])) {
927
+ handlers.push(this.resolvePriority({
928
+ effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
929
+ }, callbackName));
930
+ }
818
931
  }
819
932
  const species = pokemon.baseSpecies;
820
933
  // @ts-ignore - dynamic lookup
@@ -837,13 +950,13 @@ class Battle {
837
950
  state: slotConditionState,
838
951
  end: side.removeSlotCondition,
839
952
  endCallArgs: [side, pokemon, slotCondition.id],
840
- effectHolder: side,
953
+ effectHolder: pokemon,
841
954
  }, callbackName));
842
955
  }
843
956
  }
844
957
  return handlers;
845
958
  }
846
- findBattleEventHandlers(callbackName, getKey) {
959
+ findBattleEventHandlers(callbackName, getKey, customHolder) {
847
960
  const handlers = [];
848
961
  let callback;
849
962
  const format = this.format;
@@ -851,15 +964,15 @@ class Battle {
851
964
  callback = format[callbackName];
852
965
  if (callback !== undefined || (getKey && this.formatData[getKey])) {
853
966
  handlers.push(this.resolvePriority({
854
- effect: format, callback, state: this.formatData, end: null, effectHolder: this,
967
+ effect: format, callback, state: this.formatData, end: null, effectHolder: customHolder || this,
855
968
  }, callbackName));
856
969
  }
857
970
  if (this.events && (callback = this.events[callbackName]) !== undefined) {
858
971
  for (const handler of callback) {
859
972
  const state = (handler.target.effectType === 'Format') ? this.formatData : null;
860
973
  handlers.push({
861
- effect: handler.target, callback: handler.callback, state, end: null,
862
- effectHolder: this, priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
974
+ effect: handler.target, callback: handler.callback, state, end: null, effectHolder: customHolder || this,
975
+ priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
863
976
  });
864
977
  }
865
978
  }
@@ -1002,11 +1115,11 @@ class Battle {
1002
1115
  }
1003
1116
  return pokemonList;
1004
1117
  }
1005
- getAllActive() {
1118
+ getAllActive(includeFainted) {
1006
1119
  const pokemonList = [];
1007
1120
  for (const side of this.sides) {
1008
1121
  for (const pokemon of side.active) {
1009
- if (pokemon && !pokemon.fainted) {
1122
+ if (pokemon && (includeFainted || !pokemon.fainted)) {
1010
1123
  pokemonList.push(pokemon);
1011
1124
  }
1012
1125
  }
@@ -1401,24 +1514,6 @@ class Battle {
1401
1514
  this.quickClawRoll = this.randomChance(60, 256);
1402
1515
  if (this.gen === 3)
1403
1516
  this.quickClawRoll = this.randomChance(1, 5);
1404
- // Crazyhouse Progress checker because sidebars has trouble keeping track of Pokemon.
1405
- // Please remove me once there is client support.
1406
- if (this.ruleTable.has('crazyhouserule')) {
1407
- for (const side of this.sides) {
1408
- let buf = `raw|${lib_1.Utils.escapeHTML(side.name)}'s team:<br />`;
1409
- for (const pokemon of side.pokemon) {
1410
- if (!buf.endsWith('<br />'))
1411
- buf += '/</span>&#8203;';
1412
- if (pokemon.fainted) {
1413
- buf += `<span style="white-space:nowrap;"><span style="opacity:.3"><psicon pokemon="${pokemon.species.id}" /></span>`;
1414
- }
1415
- else {
1416
- buf += `<span style="white-space:nowrap"><psicon pokemon="${pokemon.species.id}" />`;
1417
- }
1418
- }
1419
- this.add(`${buf}</span>`);
1420
- }
1421
- }
1422
1517
  this.makeRequest('move');
1423
1518
  }
1424
1519
  maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide) {
@@ -2149,12 +2244,23 @@ class Battle {
2149
2244
  pokemon.side.totalFainted++;
2150
2245
  this.runEvent('Faint', pokemon, faintData.source, faintData.effect);
2151
2246
  this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon);
2247
+ this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
2248
+ if (pokemon.regressionForme) {
2249
+ // before clearing volatiles
2250
+ pokemon.baseSpecies = this.dex.species.get(pokemon.set.species || pokemon.set.name);
2251
+ pokemon.baseAbility = (0, dex_1.toID)(pokemon.set.ability);
2252
+ }
2152
2253
  pokemon.clearVolatile(false);
2153
2254
  pokemon.fainted = true;
2154
2255
  pokemon.illusion = null;
2155
2256
  pokemon.isActive = false;
2156
2257
  pokemon.isStarted = false;
2157
2258
  delete pokemon.terastallized;
2259
+ if (pokemon.regressionForme) {
2260
+ // after clearing volatiles
2261
+ pokemon.details = pokemon.getUpdatedDetails();
2262
+ pokemon.regressionForme = false;
2263
+ }
2158
2264
  pokemon.side.faintedThisTurn = pokemon;
2159
2265
  if (this.faintQueue.length >= faintQueueLeft)
2160
2266
  checkWin = true;
@@ -2274,8 +2380,7 @@ class Battle {
2274
2380
  if (!species)
2275
2381
  continue;
2276
2382
  pokemon.baseSpecies = rawSpecies;
2277
- pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
2278
- (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
2383
+ pokemon.details = pokemon.getUpdatedDetails();
2279
2384
  pokemon.setAbility(species.abilities['0'], null, true);
2280
2385
  pokemon.baseAbility = pokemon.ability;
2281
2386
  const behemothMove = {
@@ -2428,17 +2533,9 @@ class Battle {
2428
2533
  this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
2429
2534
  action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
2430
2535
  break;
2431
- case 'runUnnerve':
2432
- this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
2433
- break;
2434
2536
  case 'runSwitch':
2435
2537
  this.actions.runSwitch(action.pokemon);
2436
2538
  break;
2437
- case 'runPrimal':
2438
- if (!action.pokemon.transformed) {
2439
- this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
2440
- }
2441
- break;
2442
2539
  case 'shift':
2443
2540
  if (!action.pokemon.isActive)
2444
2541
  return false;
@@ -2454,7 +2551,7 @@ class Battle {
2454
2551
  this.clearActiveMove(true);
2455
2552
  this.updateSpeed();
2456
2553
  residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()]);
2457
- this.residualEvent('Residual');
2554
+ this.fieldEvent('Residual');
2458
2555
  this.add('upkeep');
2459
2556
  break;
2460
2557
  }
@@ -2495,7 +2592,7 @@ class Battle {
2495
2592
  else if (this.queue.peek()?.choice === 'instaswitch') {
2496
2593
  return false;
2497
2594
  }
2498
- if (this.gen >= 5) {
2595
+ if (this.gen >= 5 && action.choice !== 'start') {
2499
2596
  this.eachEvent('Update');
2500
2597
  for (const [pokemon, originalHP] of residualPokemon) {
2501
2598
  const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
@@ -2920,6 +3017,34 @@ class Battle {
2920
3017
  getOverflowedTurnCount() {
2921
3018
  return this.gen >= 8 ? (this.turn - 1) % 256 : this.turn - 1;
2922
3019
  }
3020
+ initEffectState(obj, effectOrder) {
3021
+ if (!obj.id)
3022
+ obj.id = '';
3023
+ if (effectOrder !== undefined) {
3024
+ obj.effectOrder = effectOrder;
3025
+ }
3026
+ else if (obj.id && obj.target && (!(obj.target instanceof pokemon_1.Pokemon) || obj.target.isActive)) {
3027
+ obj.effectOrder = this.effectOrder++;
3028
+ }
3029
+ else {
3030
+ obj.effectOrder = 0;
3031
+ }
3032
+ return obj;
3033
+ }
3034
+ clearEffectState(state) {
3035
+ state.id = '';
3036
+ for (const k in state) {
3037
+ if (k === 'id' || k === 'target') {
3038
+ continue;
3039
+ }
3040
+ else if (k === 'effectOrder') {
3041
+ state.effectOrder = 0;
3042
+ }
3043
+ else {
3044
+ delete state[k];
3045
+ }
3046
+ }
3047
+ }
2923
3048
  destroy() {
2924
3049
  // deallocate ourself
2925
3050
  // deallocate children and get rid of references to them