@pkmn/sim 0.9.26 → 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 (189) hide show
  1. package/build/cjs/config/formats.js +79 -86
  2. package/build/cjs/config/formats.js.map +1 -1
  3. package/build/cjs/data/abilities.js +100 -116
  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 +12 -12
  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/gen2/moves.js +1 -1
  19. package/build/cjs/data/mods/gen2/moves.js.map +1 -1
  20. package/build/cjs/data/mods/gen3/abilities.js +0 -2
  21. package/build/cjs/data/mods/gen3/abilities.js.map +1 -1
  22. package/build/cjs/data/mods/gen3/moves.js.map +1 -1
  23. package/build/cjs/data/mods/gen4/abilities.js +1 -1
  24. package/build/cjs/data/mods/gen4/abilities.js.map +1 -1
  25. package/build/cjs/data/mods/gen4/formats-data.js +1 -1
  26. package/build/cjs/data/mods/gen4/formats-data.js.map +1 -1
  27. package/build/cjs/data/mods/gen4/moves.js +88 -0
  28. package/build/cjs/data/mods/gen4/moves.js.map +1 -1
  29. package/build/cjs/data/mods/gen4/scripts.js +26 -0
  30. package/build/cjs/data/mods/gen4/scripts.js.map +1 -1
  31. package/build/cjs/data/mods/gen5/conditions.js +1 -2
  32. package/build/cjs/data/mods/gen5/conditions.js.map +1 -1
  33. package/build/cjs/data/mods/gen5/formats-data.js +2 -2
  34. package/build/cjs/data/mods/gen5/formats-data.js.map +1 -1
  35. package/build/cjs/data/mods/gen6/moves.js.map +1 -1
  36. package/build/cjs/data/mods/gen8/abilities.js +0 -2
  37. package/build/cjs/data/mods/gen8/abilities.js.map +1 -1
  38. package/build/cjs/data/mods/gen8/moves.js +1 -1
  39. package/build/cjs/data/mods/gen8/moves.js.map +1 -1
  40. package/build/cjs/data/moves.js +22 -18
  41. package/build/cjs/data/moves.js.map +1 -1
  42. package/build/cjs/data/pokemongo.js +1 -1
  43. package/build/cjs/data/text/moves.js +1 -1
  44. package/build/cjs/data/text/moves.js.map +1 -1
  45. package/build/cjs/lib/utils.d.ts +8 -0
  46. package/build/cjs/lib/utils.js +21 -2
  47. package/build/cjs/lib/utils.js.map +1 -1
  48. package/build/cjs/sim/battle-actions.js +30 -39
  49. package/build/cjs/sim/battle-actions.js.map +1 -1
  50. package/build/cjs/sim/battle-queue.d.ts +1 -1
  51. package/build/cjs/sim/battle-queue.js +0 -2
  52. package/build/cjs/sim/battle-queue.js.map +1 -1
  53. package/build/cjs/sim/battle-stream.js +2 -3
  54. package/build/cjs/sim/battle-stream.js.map +1 -1
  55. package/build/cjs/sim/battle.d.ts +13 -7
  56. package/build/cjs/sim/battle.js +197 -70
  57. package/build/cjs/sim/battle.js.map +1 -1
  58. package/build/cjs/sim/dex-abilities.d.ts +0 -1
  59. package/build/cjs/sim/dex-abilities.js +2 -2
  60. package/build/cjs/sim/dex-abilities.js.map +1 -1
  61. package/build/cjs/sim/dex-conditions.d.ts +11 -3
  62. package/build/cjs/sim/dex-conditions.js +2 -2
  63. package/build/cjs/sim/dex-conditions.js.map +1 -1
  64. package/build/cjs/sim/dex-data.js +7 -7
  65. package/build/cjs/sim/dex-data.js.map +1 -1
  66. package/build/cjs/sim/dex-formats.d.ts +2 -1
  67. package/build/cjs/sim/dex-formats.js.map +1 -1
  68. package/build/cjs/sim/dex-items.d.ts +3 -1
  69. package/build/cjs/sim/dex-items.js +3 -2
  70. package/build/cjs/sim/dex-items.js.map +1 -1
  71. package/build/cjs/sim/dex-moves.js +4 -4
  72. package/build/cjs/sim/dex-moves.js.map +1 -1
  73. package/build/cjs/sim/exported-global-types.d.ts +6 -0
  74. package/build/cjs/sim/field.js +9 -9
  75. package/build/cjs/sim/field.js.map +1 -1
  76. package/build/cjs/sim/global-types.d.ts +6 -0
  77. package/build/cjs/sim/pokemon.d.ts +8 -4
  78. package/build/cjs/sim/pokemon.js +62 -47
  79. package/build/cjs/sim/pokemon.js.map +1 -1
  80. package/build/cjs/sim/prng.d.ts +57 -18
  81. package/build/cjs/sim/prng.js +171 -38
  82. package/build/cjs/sim/prng.js.map +1 -1
  83. package/build/cjs/sim/side.js +9 -6
  84. package/build/cjs/sim/side.js.map +1 -1
  85. package/build/cjs/sim/state.js +1 -1
  86. package/build/cjs/sim/state.js.map +1 -1
  87. package/build/cjs/sim/team-validator.js +16 -17
  88. package/build/cjs/sim/team-validator.js.map +1 -1
  89. package/build/cjs/sim/tools/exhaustive-runner.js +11 -12
  90. package/build/cjs/sim/tools/exhaustive-runner.js.map +1 -1
  91. package/build/cjs/sim/tools/random-player-ai.js +5 -5
  92. package/build/cjs/sim/tools/random-player-ai.js.map +1 -1
  93. package/build/cjs/sim/tools/runner.js +7 -8
  94. package/build/cjs/sim/tools/runner.js.map +1 -1
  95. package/build/esm/config/formats.mjs +79 -86
  96. package/build/esm/config/formats.mjs.map +1 -1
  97. package/build/esm/data/abilities.mjs +100 -116
  98. package/build/esm/data/abilities.mjs.map +1 -1
  99. package/build/esm/data/aliases.mjs +3 -2
  100. package/build/esm/data/aliases.mjs.map +1 -1
  101. package/build/esm/data/conditions.mjs +3 -3
  102. package/build/esm/data/conditions.mjs.map +1 -1
  103. package/build/esm/data/formats-data.mjs +12 -12
  104. package/build/esm/data/formats-data.mjs.map +1 -1
  105. package/build/esm/data/items.mjs +55 -25
  106. package/build/esm/data/items.mjs.map +1 -1
  107. package/build/esm/data/legality.mjs +275 -23
  108. package/build/esm/data/legality.mjs.map +1 -1
  109. package/build/esm/data/mods/gen1/moves.mjs.map +1 -1
  110. package/build/esm/data/mods/gen1/scripts.mjs +2 -0
  111. package/build/esm/data/mods/gen1/scripts.mjs.map +1 -1
  112. package/build/esm/data/mods/gen2/moves.mjs +1 -1
  113. package/build/esm/data/mods/gen2/moves.mjs.map +1 -1
  114. package/build/esm/data/mods/gen3/abilities.mjs +0 -2
  115. package/build/esm/data/mods/gen3/abilities.mjs.map +1 -1
  116. package/build/esm/data/mods/gen3/moves.mjs.map +1 -1
  117. package/build/esm/data/mods/gen4/abilities.mjs +1 -1
  118. package/build/esm/data/mods/gen4/abilities.mjs.map +1 -1
  119. package/build/esm/data/mods/gen4/formats-data.mjs +1 -1
  120. package/build/esm/data/mods/gen4/formats-data.mjs.map +1 -1
  121. package/build/esm/data/mods/gen4/moves.mjs +88 -0
  122. package/build/esm/data/mods/gen4/moves.mjs.map +1 -1
  123. package/build/esm/data/mods/gen4/scripts.mjs +26 -0
  124. package/build/esm/data/mods/gen4/scripts.mjs.map +1 -1
  125. package/build/esm/data/mods/gen5/conditions.mjs +1 -2
  126. package/build/esm/data/mods/gen5/conditions.mjs.map +1 -1
  127. package/build/esm/data/mods/gen5/formats-data.mjs +2 -2
  128. package/build/esm/data/mods/gen5/formats-data.mjs.map +1 -1
  129. package/build/esm/data/mods/gen6/moves.mjs.map +1 -1
  130. package/build/esm/data/mods/gen8/abilities.mjs +0 -2
  131. package/build/esm/data/mods/gen8/abilities.mjs.map +1 -1
  132. package/build/esm/data/mods/gen8/moves.mjs +1 -1
  133. package/build/esm/data/mods/gen8/moves.mjs.map +1 -1
  134. package/build/esm/data/moves.mjs +22 -18
  135. package/build/esm/data/moves.mjs.map +1 -1
  136. package/build/esm/data/pokemongo.mjs +1 -1
  137. package/build/esm/data/text/moves.mjs +1 -1
  138. package/build/esm/data/text/moves.mjs.map +1 -1
  139. package/build/esm/lib/utils.d.mts +8 -0
  140. package/build/esm/lib/utils.mjs +18 -2
  141. package/build/esm/lib/utils.mjs.map +1 -1
  142. package/build/esm/sim/battle-actions.mjs +30 -39
  143. package/build/esm/sim/battle-actions.mjs.map +1 -1
  144. package/build/esm/sim/battle-queue.d.mts +1 -1
  145. package/build/esm/sim/battle-queue.mjs +0 -2
  146. package/build/esm/sim/battle-queue.mjs.map +1 -1
  147. package/build/esm/sim/battle-stream.mjs +2 -3
  148. package/build/esm/sim/battle-stream.mjs.map +1 -1
  149. package/build/esm/sim/battle.d.mts +13 -7
  150. package/build/esm/sim/battle.mjs +196 -69
  151. package/build/esm/sim/battle.mjs.map +1 -1
  152. package/build/esm/sim/dex-abilities.d.mts +0 -1
  153. package/build/esm/sim/dex-abilities.mjs +1 -1
  154. package/build/esm/sim/dex-abilities.mjs.map +1 -1
  155. package/build/esm/sim/dex-conditions.d.mts +11 -3
  156. package/build/esm/sim/dex-conditions.mjs +1 -1
  157. package/build/esm/sim/dex-conditions.mjs.map +1 -1
  158. package/build/esm/sim/dex-data.mjs +1 -1
  159. package/build/esm/sim/dex-data.mjs.map +1 -1
  160. package/build/esm/sim/dex-formats.d.mts +2 -1
  161. package/build/esm/sim/dex-formats.mjs.map +1 -1
  162. package/build/esm/sim/dex-items.d.mts +3 -1
  163. package/build/esm/sim/dex-items.mjs +2 -1
  164. package/build/esm/sim/dex-items.mjs.map +1 -1
  165. package/build/esm/sim/dex-moves.mjs +1 -1
  166. package/build/esm/sim/dex-moves.mjs.map +1 -1
  167. package/build/esm/sim/exported-global-types.d.mts +6 -0
  168. package/build/esm/sim/field.mjs +9 -9
  169. package/build/esm/sim/field.mjs.map +1 -1
  170. package/build/esm/sim/global-types.d.mts +6 -0
  171. package/build/esm/sim/pokemon.d.mts +8 -4
  172. package/build/esm/sim/pokemon.mjs +62 -47
  173. package/build/esm/sim/pokemon.mjs.map +1 -1
  174. package/build/esm/sim/prng.d.mts +57 -18
  175. package/build/esm/sim/prng.mjs +134 -36
  176. package/build/esm/sim/prng.mjs.map +1 -1
  177. package/build/esm/sim/side.mjs +8 -5
  178. package/build/esm/sim/side.mjs.map +1 -1
  179. package/build/esm/sim/state.mjs +1 -1
  180. package/build/esm/sim/state.mjs.map +1 -1
  181. package/build/esm/sim/team-validator.mjs +10 -11
  182. package/build/esm/sim/team-validator.mjs.map +1 -1
  183. package/build/esm/sim/tools/exhaustive-runner.mjs +11 -12
  184. package/build/esm/sim/tools/exhaustive-runner.mjs.map +1 -1
  185. package/build/esm/sim/tools/random-player-ai.mjs +5 -5
  186. package/build/esm/sim/tools/random-player-ai.mjs.map +1 -1
  187. package/build/esm/sim/tools/runner.mjs +7 -8
  188. package/build/esm/sim/tools/runner.mjs.map +1 -1
  189. package/package.json +3 -2
@@ -92,15 +92,12 @@ exports.Abilities = {
92
92
  },
93
93
  airlock: {
94
94
  onSwitchIn(pokemon) {
95
- this.effectState.switchingIn = true;
95
+ // Air Lock does not activate when Skill Swapped or when Neutralizing Gas leaves the field
96
+ this.add('-ability', pokemon, 'Air Lock');
97
+ this.effect.onStart.call(this, pokemon);
96
98
  },
97
99
  onStart(pokemon) {
98
- // Air Lock does not activate when Skill Swapped or when Neutralizing Gas leaves the field
99
100
  pokemon.abilityState.ending = false; // Clear the ending flag
100
- if (this.effectState.switchingIn) {
101
- this.add('-ability', pokemon, 'Air Lock');
102
- this.effectState.switchingIn = false;
103
- }
104
101
  this.eachEvent('WeatherChange', this.effect);
105
102
  },
106
103
  onEnd(pokemon) {
@@ -262,11 +259,7 @@ exports.Abilities = {
262
259
  num: 165,
263
260
  },
264
261
  asoneglastrier: {
265
- onPreStart(pokemon) {
266
- this.add('-ability', pokemon, 'As One');
267
- this.add('-ability', pokemon, 'Unnerve');
268
- this.effectState.unnerved = true;
269
- },
262
+ onSwitchInPriority: 1,
270
263
  onStart(pokemon) {
271
264
  if (this.effectState.unnerved)
272
265
  return;
@@ -291,11 +284,7 @@ exports.Abilities = {
291
284
  num: 266,
292
285
  },
293
286
  asonespectrier: {
294
- onPreStart(pokemon) {
295
- this.add('-ability', pokemon, 'As One');
296
- this.add('-ability', pokemon, 'Unnerve');
297
- this.effectState.unnerved = true;
298
- },
287
+ onSwitchInPriority: 1,
299
288
  onStart(pokemon) {
300
289
  if (this.effectState.unnerved)
301
290
  return;
@@ -568,15 +557,12 @@ exports.Abilities = {
568
557
  },
569
558
  cloudnine: {
570
559
  onSwitchIn(pokemon) {
571
- this.effectState.switchingIn = true;
560
+ // Cloud Nine does not activate when Skill Swapped or when Neutralizing Gas leaves the field
561
+ this.add('-ability', pokemon, 'Cloud Nine');
562
+ this.effect.onStart.call(this, pokemon);
572
563
  },
573
564
  onStart(pokemon) {
574
- // Cloud Nine does not activate when Skill Swapped or when Neutralizing Gas leaves the field
575
565
  pokemon.abilityState.ending = false; // Clear the ending flag
576
- if (this.effectState.switchingIn) {
577
- this.add('-ability', pokemon, 'Cloud Nine');
578
- this.effectState.switchingIn = false;
579
- }
580
566
  this.eachEvent('WeatherChange', this.effect);
581
567
  },
582
568
  onEnd(pokemon) {
@@ -630,10 +616,22 @@ exports.Abilities = {
630
616
  num: 213,
631
617
  },
632
618
  commander: {
619
+ onAnySwitchInPriority: -2,
620
+ onAnySwitchIn() {
621
+ this.effect.onUpdate.call(this, this.effectState.target);
622
+ },
623
+ onStart(pokemon) {
624
+ this.effect.onUpdate.call(this, pokemon);
625
+ },
633
626
  onUpdate(pokemon) {
634
627
  if (this.gameType !== 'doubles')
635
628
  return;
629
+ // don't run between when a Pokemon switches in and the resulting onSwitchIn event
630
+ if (this.queue.peek()?.choice === 'runSwitch')
631
+ return;
636
632
  const ally = pokemon.allies()[0];
633
+ if (pokemon.switchFlag || ally?.switchFlag)
634
+ return;
637
635
  if (!ally || pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') {
638
636
  // Handle any edge cases
639
637
  if (pokemon.getVolatile('commanding'))
@@ -719,6 +717,7 @@ exports.Abilities = {
719
717
  num: 212,
720
718
  },
721
719
  costar: {
720
+ onSwitchInPriority: -2,
722
721
  onStart(pokemon) {
723
722
  const ally = pokemon.allies()[0];
724
723
  if (!ally)
@@ -1101,12 +1100,8 @@ exports.Abilities = {
1101
1100
  },
1102
1101
  drizzle: {
1103
1102
  onStart(source) {
1104
- for (const action of this.queue) {
1105
- if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'kyogre')
1106
- return;
1107
- if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal')
1108
- break;
1109
- }
1103
+ if (source.species.id === 'kyogre' && source.item === 'blueorb')
1104
+ return;
1110
1105
  this.field.setWeather('raindance');
1111
1106
  },
1112
1107
  flags: {},
@@ -1116,12 +1111,8 @@ exports.Abilities = {
1116
1111
  },
1117
1112
  drought: {
1118
1113
  onStart(source) {
1119
- for (const action of this.queue) {
1120
- if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'groudon')
1121
- return;
1122
- if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal')
1123
- break;
1124
- }
1114
+ if (source.species.id === 'groudon' && source.item === 'redorb')
1115
+ return;
1125
1116
  this.field.setWeather('sunnyday');
1126
1117
  },
1127
1118
  flags: {},
@@ -1221,14 +1212,12 @@ exports.Abilities = {
1221
1212
  },
1222
1213
  embodyaspectcornerstone: {
1223
1214
  onStart(pokemon) {
1224
- if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied) {
1225
- this.effectState.embodied = true;
1215
+ if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' &&
1216
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1217
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1226
1218
  this.boost({ def: 1 }, pokemon);
1227
1219
  }
1228
1220
  },
1229
- onSwitchIn() {
1230
- delete this.effectState.embodied;
1231
- },
1232
1221
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1233
1222
  name: "Embody Aspect (Cornerstone)",
1234
1223
  rating: 3.5,
@@ -1236,14 +1225,12 @@ exports.Abilities = {
1236
1225
  },
1237
1226
  embodyaspecthearthflame: {
1238
1227
  onStart(pokemon) {
1239
- if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied) {
1240
- this.effectState.embodied = true;
1228
+ if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' &&
1229
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1230
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1241
1231
  this.boost({ atk: 1 }, pokemon);
1242
1232
  }
1243
1233
  },
1244
- onSwitchIn() {
1245
- delete this.effectState.embodied;
1246
- },
1247
1234
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1248
1235
  name: "Embody Aspect (Hearthflame)",
1249
1236
  rating: 3.5,
@@ -1251,14 +1238,12 @@ exports.Abilities = {
1251
1238
  },
1252
1239
  embodyaspectteal: {
1253
1240
  onStart(pokemon) {
1254
- if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied) {
1255
- this.effectState.embodied = true;
1241
+ if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' &&
1242
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1243
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1256
1244
  this.boost({ spe: 1 }, pokemon);
1257
1245
  }
1258
1246
  },
1259
- onSwitchIn() {
1260
- delete this.effectState.embodied;
1261
- },
1262
1247
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1263
1248
  name: "Embody Aspect (Teal)",
1264
1249
  rating: 3.5,
@@ -1266,14 +1251,12 @@ exports.Abilities = {
1266
1251
  },
1267
1252
  embodyaspectwellspring: {
1268
1253
  onStart(pokemon) {
1269
- if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied) {
1270
- this.effectState.embodied = true;
1254
+ if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' &&
1255
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1256
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1271
1257
  this.boost({ spd: 1 }, pokemon);
1272
1258
  }
1273
1259
  },
1274
- onSwitchIn() {
1275
- delete this.effectState.embodied;
1276
- },
1277
1260
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1278
1261
  name: "Embody Aspect (Wellspring)",
1279
1262
  rating: 3.5,
@@ -1396,6 +1379,7 @@ exports.Abilities = {
1396
1379
  num: 18,
1397
1380
  },
1398
1381
  flowergift: {
1382
+ onSwitchInPriority: -2,
1399
1383
  onStart(pokemon) {
1400
1384
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
1401
1385
  },
@@ -1491,6 +1475,7 @@ exports.Abilities = {
1491
1475
  num: 218,
1492
1476
  },
1493
1477
  forecast: {
1478
+ onSwitchInPriority: -2,
1494
1479
  onStart(pokemon) {
1495
1480
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
1496
1481
  },
@@ -1916,6 +1901,7 @@ exports.Abilities = {
1916
1901
  num: 118,
1917
1902
  },
1918
1903
  hospitality: {
1904
+ onSwitchInPriority: -2,
1919
1905
  onStart(pokemon) {
1920
1906
  for (const ally of pokemon.adjacentAllies()) {
1921
1907
  this.heal(ally.baseMaxhp / 4, ally, pokemon);
@@ -2013,6 +1999,7 @@ exports.Abilities = {
2013
1999
  num: 115,
2014
2000
  },
2015
2001
  iceface: {
2002
+ onSwitchInPriority: -2,
2016
2003
  onStart(pokemon) {
2017
2004
  if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') {
2018
2005
  this.add('-activate', pokemon, 'ability: Ice Face');
@@ -2131,8 +2118,7 @@ exports.Abilities = {
2131
2118
  if (pokemon.illusion) {
2132
2119
  this.debug('illusion cleared');
2133
2120
  pokemon.illusion = null;
2134
- const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
2135
- (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
2121
+ const details = pokemon.getUpdatedDetails();
2136
2122
  this.add('replace', pokemon, details);
2137
2123
  this.add('-end', pokemon, 'Illusion');
2138
2124
  if (this.ruleTable.has('illusionlevelmod')) {
@@ -2170,20 +2156,14 @@ exports.Abilities = {
2170
2156
  },
2171
2157
  imposter: {
2172
2158
  onSwitchIn(pokemon) {
2173
- this.effectState.switchingIn = true;
2174
- },
2175
- onStart(pokemon) {
2176
2159
  // Imposter does not activate when Skill Swapped or when Neutralizing Gas leaves the field
2177
- if (!this.effectState.switchingIn)
2178
- return;
2179
- // copies across in doubles/triples
2160
+ // Imposter copies across in doubles/triples
2180
2161
  // (also copies across in multibattle and diagonally in free-for-all,
2181
2162
  // but side.foe already takes care of those)
2182
2163
  const target = pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
2183
2164
  if (target) {
2184
2165
  pokemon.transformInto(target, this.dex.abilities.get('imposter'));
2185
2166
  }
2186
- this.effectState.switchingIn = false;
2187
2167
  },
2188
2168
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
2189
2169
  name: "Imposter",
@@ -2342,6 +2322,9 @@ exports.Abilities = {
2342
2322
  num: 51,
2343
2323
  },
2344
2324
  klutz: {
2325
+ // Klutz isn't technically active immediately in-game, but it activates early enough to beat all items
2326
+ // we should keep an eye out in future gens for items that activate on switch-in before Unnerve
2327
+ onSwitchInPriority: 1,
2345
2328
  // Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
2346
2329
  onStart(pokemon) {
2347
2330
  this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
@@ -2380,7 +2363,7 @@ exports.Abilities = {
2380
2363
  },
2381
2364
  libero: {
2382
2365
  onPrepareHit(source, target, move) {
2383
- if (this.effectState.libero)
2366
+ if (this.effectState.libero === source.previouslySwitchedIn)
2384
2367
  return;
2385
2368
  if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove)
2386
2369
  return;
@@ -2388,13 +2371,10 @@ exports.Abilities = {
2388
2371
  if (type && type !== '???' && source.getTypes().join() !== type) {
2389
2372
  if (!source.setType(type))
2390
2373
  return;
2391
- this.effectState.libero = true;
2374
+ this.effectState.libero = source.previouslySwitchedIn;
2392
2375
  this.add('-start', source, 'typechange', type, '[from] ability: Libero');
2393
2376
  }
2394
2377
  },
2395
- onSwitchIn() {
2396
- delete this.effectState.libero;
2397
- },
2398
2378
  flags: {},
2399
2379
  name: "Libero",
2400
2380
  rating: 4,
@@ -2645,6 +2625,7 @@ exports.Abilities = {
2645
2625
  num: 196,
2646
2626
  },
2647
2627
  mimicry: {
2628
+ onSwitchInPriority: -1,
2648
2629
  onStart(pokemon) {
2649
2630
  this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon);
2650
2631
  },
@@ -2981,7 +2962,8 @@ exports.Abilities = {
2981
2962
  },
2982
2963
  neutralizinggas: {
2983
2964
  // Ability suppression implemented in sim/pokemon.ts:Pokemon#ignoringAbility
2984
- onPreStart(pokemon) {
2965
+ onSwitchInPriority: 2,
2966
+ onSwitchIn(pokemon) {
2985
2967
  this.add('-ability', pokemon, 'Neutralizing Gas');
2986
2968
  pokemon.abilityState.ending = false;
2987
2969
  const strongWeathers = ['desolateland', 'primordialsea', 'deltastream'];
@@ -3122,17 +3104,38 @@ exports.Abilities = {
3122
3104
  onFoeAfterBoost(boost, target, source, effect) {
3123
3105
  if (effect?.name === 'Opportunist' || effect?.name === 'Mirror Herb')
3124
3106
  return;
3125
- const pokemon = this.effectState.target;
3126
- const positiveBoosts = {};
3107
+ if (!this.effectState.boosts)
3108
+ this.effectState.boosts = {};
3109
+ const boostPlus = this.effectState.boosts;
3127
3110
  let i;
3128
3111
  for (i in boost) {
3129
3112
  if (boost[i] > 0) {
3130
- positiveBoosts[i] = boost[i];
3113
+ boostPlus[i] = (boostPlus[i] || 0) + boost[i];
3131
3114
  }
3132
3115
  }
3133
- if (Object.keys(positiveBoosts).length < 1)
3116
+ },
3117
+ onAnySwitchInPriority: -3,
3118
+ onAnySwitchIn() {
3119
+ if (!this.effectState.boosts)
3134
3120
  return;
3135
- this.boost(positiveBoosts, pokemon);
3121
+ this.boost(this.effectState.boosts, this.effectState.target);
3122
+ delete this.effectState.boosts;
3123
+ },
3124
+ onAnyAfterMove() {
3125
+ if (!this.effectState.boosts)
3126
+ return;
3127
+ this.boost(this.effectState.boosts, this.effectState.target);
3128
+ delete this.effectState.boosts;
3129
+ },
3130
+ onResidualOrder: 29,
3131
+ onResidual(pokemon) {
3132
+ if (!this.effectState.boosts)
3133
+ return;
3134
+ this.boost(this.effectState.boosts, this.effectState.target);
3135
+ delete this.effectState.boosts;
3136
+ },
3137
+ onEnd() {
3138
+ delete this.effectState.boosts;
3136
3139
  },
3137
3140
  flags: {},
3138
3141
  name: "Opportunist",
@@ -3259,11 +3262,8 @@ exports.Abilities = {
3259
3262
  pokemon.cureStatus();
3260
3263
  }
3261
3264
  },
3262
- onAllySwitchIn(pokemon) {
3263
- if (['psn', 'tox'].includes(pokemon.status)) {
3264
- this.add('-activate', this.effectState.target, 'ability: Pastel Veil');
3265
- pokemon.cureStatus();
3266
- }
3265
+ onAnySwitchIn() {
3266
+ this.effect.onStart.call(this, this.effectState.target);
3267
3267
  },
3268
3268
  onSetStatus(status, target, source, effect) {
3269
3269
  if (!['psn', 'tox'].includes(status.id))
@@ -3570,7 +3570,7 @@ exports.Abilities = {
3570
3570
  },
3571
3571
  protean: {
3572
3572
  onPrepareHit(source, target, move) {
3573
- if (this.effectState.protean)
3573
+ if (this.effectState.protean === source.previouslySwitchedIn)
3574
3574
  return;
3575
3575
  if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove)
3576
3576
  return;
@@ -3578,19 +3578,17 @@ exports.Abilities = {
3578
3578
  if (type && type !== '???' && source.getTypes().join() !== type) {
3579
3579
  if (!source.setType(type))
3580
3580
  return;
3581
- this.effectState.protean = true;
3581
+ this.effectState.protean = source.previouslySwitchedIn;
3582
3582
  this.add('-start', source, 'typechange', type, '[from] ability: Protean');
3583
3583
  }
3584
3584
  },
3585
- onSwitchIn(pokemon) {
3586
- delete this.effectState.protean;
3587
- },
3588
3585
  flags: {},
3589
3586
  name: "Protean",
3590
3587
  rating: 4,
3591
3588
  num: 168,
3592
3589
  },
3593
3590
  protosynthesis: {
3591
+ onSwitchInPriority: -2,
3594
3592
  onStart(pokemon) {
3595
3593
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
3596
3594
  },
@@ -3599,8 +3597,7 @@ exports.Abilities = {
3599
3597
  if (this.field.isWeather('sunnyday')) {
3600
3598
  pokemon.addVolatile('protosynthesis');
3601
3599
  }
3602
- else if (!pokemon.volatiles['protosynthesis']?.fromBooster && this.field.weather !== 'sunnyday') {
3603
- // Protosynthesis will not deactivite if Sun is suppressed, hence the direct ID check (isWeather respects supression)
3600
+ else if (!pokemon.volatiles['protosynthesis']?.fromBooster && !this.field.isWeather('sunnyday')) {
3604
3601
  pokemon.removeVolatile('protosynthesis');
3605
3602
  }
3606
3603
  },
@@ -3735,6 +3732,7 @@ exports.Abilities = {
3735
3732
  num: 272,
3736
3733
  },
3737
3734
  quarkdrive: {
3735
+ onSwitchInPriority: -2,
3738
3736
  onStart(pokemon) {
3739
3737
  this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon);
3740
3738
  },
@@ -4150,6 +4148,7 @@ exports.Abilities = {
4150
4148
  num: 157,
4151
4149
  },
4152
4150
  schooling: {
4151
+ onSwitchInPriority: -1,
4153
4152
  onStart(pokemon) {
4154
4153
  if (pokemon.baseSpecies.baseSpecies !== 'Wishiwashi' || pokemon.level < 20 || pokemon.transformed)
4155
4154
  return;
@@ -4353,6 +4352,7 @@ exports.Abilities = {
4353
4352
  num: 19,
4354
4353
  },
4355
4354
  shieldsdown: {
4355
+ onSwitchInPriority: -1,
4356
4356
  onStart(pokemon) {
4357
4357
  if (pokemon.baseSpecies.baseSpecies !== 'Minior' || pokemon.transformed)
4358
4358
  return;
@@ -5095,21 +5095,7 @@ exports.Abilities = {
5095
5095
  num: 309,
5096
5096
  },
5097
5097
  terashell: {
5098
- onEffectiveness(typeMod, target, type, move) {
5099
- if (!target || target.species.name !== 'Terapagos-Terastal')
5100
- return;
5101
- if (this.effectState.resisted)
5102
- return -1; // all hits of multi-hit move should be not very effective
5103
- if (move.category === 'Status' || move.id === 'struggle')
5104
- return;
5105
- if (!target.runImmunity(move.type))
5106
- return; // immunity has priority
5107
- if (target.hp < target.maxhp)
5108
- return;
5109
- this.add('-activate', target, 'ability: Tera Shell');
5110
- this.effectState.resisted = true;
5111
- return -1;
5112
- },
5098
+ // effectiveness implemented in sim/pokemon.ts:Pokemon#runEffectiveness
5113
5099
  onAnyAfterMove() {
5114
5100
  this.effectState.resisted = false;
5115
5101
  },
@@ -5119,12 +5105,14 @@ exports.Abilities = {
5119
5105
  num: 308,
5120
5106
  },
5121
5107
  terashift: {
5122
- onPreStart(pokemon) {
5108
+ onSwitchInPriority: 2,
5109
+ onSwitchIn(pokemon) {
5123
5110
  if (pokemon.baseSpecies.baseSpecies !== 'Terapagos')
5124
5111
  return;
5125
5112
  if (pokemon.species.forme !== 'Terastal') {
5126
5113
  this.add('-activate', pokemon, 'ability: Tera Shift');
5127
5114
  pokemon.formeChange('Terapagos-Terastal', this.effect, true);
5115
+ pokemon.regressionForme = false;
5128
5116
  pokemon.baseMaxhp = Math.floor(Math.floor(2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100) * pokemon.level / 100 + 10);
5129
5117
  const newMaxHP = pokemon.baseMaxhp;
5130
5118
  pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
@@ -5280,19 +5268,23 @@ exports.Abilities = {
5280
5268
  },
5281
5269
  trace: {
5282
5270
  onStart(pokemon) {
5271
+ this.effectState.seek = true;
5283
5272
  // n.b. only affects Hackmons
5284
5273
  // interaction with No Ability is complicated: https://www.smogon.com/forums/threads/pokemon-sun-moon-battle-mechanics-research.3586701/page-76#post-7790209
5285
5274
  if (pokemon.adjacentFoes().some(foeActive => foeActive.ability === 'noability')) {
5286
- this.effectState.gaveUp = true;
5275
+ this.effectState.seek = false;
5287
5276
  }
5288
5277
  // interaction with Ability Shield is similar to No Ability
5289
5278
  if (pokemon.hasItem('Ability Shield')) {
5290
5279
  this.add('-block', pokemon, 'item: Ability Shield');
5291
- this.effectState.gaveUp = true;
5280
+ this.effectState.seek = false;
5281
+ }
5282
+ if (this.effectState.seek) {
5283
+ this.singleEvent('Update', this.effect, this.effectState, pokemon);
5292
5284
  }
5293
5285
  },
5294
5286
  onUpdate(pokemon) {
5295
- if (!pokemon.isStarted || this.effectState.gaveUp)
5287
+ if (!this.effectState.seek)
5296
5288
  return;
5297
5289
  const possibleTargets = pokemon.adjacentFoes().filter(target => !target.getAbility().flags['notrace'] && target.ability !== 'noability');
5298
5290
  if (!possibleTargets.length)
@@ -5418,10 +5410,7 @@ exports.Abilities = {
5418
5410
  num: 84,
5419
5411
  },
5420
5412
  unnerve: {
5421
- onPreStart(pokemon) {
5422
- this.add('-ability', pokemon, 'Unnerve');
5423
- this.effectState.unnerved = true;
5424
- },
5413
+ onSwitchInPriority: 1,
5425
5414
  onStart(pokemon) {
5426
5415
  if (this.effectState.unnerved)
5427
5416
  return;
@@ -5828,15 +5817,10 @@ exports.Abilities = {
5828
5817
  return;
5829
5818
  if (pokemon.species.forme !== 'Hero') {
5830
5819
  pokemon.formeChange('Palafin-Hero', this.effect, true);
5820
+ pokemon.regressionForme = false;
5831
5821
  }
5832
5822
  },
5833
- onSwitchIn() {
5834
- this.effectState.switchingIn = true;
5835
- },
5836
- onStart(pokemon) {
5837
- if (!this.effectState.switchingIn)
5838
- return;
5839
- this.effectState.switchingIn = false;
5823
+ onSwitchIn(pokemon) {
5840
5824
  if (pokemon.baseSpecies.baseSpecies !== 'Palafin')
5841
5825
  return;
5842
5826
  if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === 'Hero') {