@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
@@ -89,15 +89,12 @@ export const Abilities = {
89
89
  },
90
90
  airlock: {
91
91
  onSwitchIn(pokemon) {
92
- this.effectState.switchingIn = true;
92
+ // Air Lock does not activate when Skill Swapped or when Neutralizing Gas leaves the field
93
+ this.add('-ability', pokemon, 'Air Lock');
94
+ this.effect.onStart.call(this, pokemon);
93
95
  },
94
96
  onStart(pokemon) {
95
- // Air Lock does not activate when Skill Swapped or when Neutralizing Gas leaves the field
96
97
  pokemon.abilityState.ending = false; // Clear the ending flag
97
- if (this.effectState.switchingIn) {
98
- this.add('-ability', pokemon, 'Air Lock');
99
- this.effectState.switchingIn = false;
100
- }
101
98
  this.eachEvent('WeatherChange', this.effect);
102
99
  },
103
100
  onEnd(pokemon) {
@@ -259,11 +256,7 @@ export const Abilities = {
259
256
  num: 165,
260
257
  },
261
258
  asoneglastrier: {
262
- onPreStart(pokemon) {
263
- this.add('-ability', pokemon, 'As One');
264
- this.add('-ability', pokemon, 'Unnerve');
265
- this.effectState.unnerved = true;
266
- },
259
+ onSwitchInPriority: 1,
267
260
  onStart(pokemon) {
268
261
  if (this.effectState.unnerved)
269
262
  return;
@@ -288,11 +281,7 @@ export const Abilities = {
288
281
  num: 266,
289
282
  },
290
283
  asonespectrier: {
291
- onPreStart(pokemon) {
292
- this.add('-ability', pokemon, 'As One');
293
- this.add('-ability', pokemon, 'Unnerve');
294
- this.effectState.unnerved = true;
295
- },
284
+ onSwitchInPriority: 1,
296
285
  onStart(pokemon) {
297
286
  if (this.effectState.unnerved)
298
287
  return;
@@ -565,15 +554,12 @@ export const Abilities = {
565
554
  },
566
555
  cloudnine: {
567
556
  onSwitchIn(pokemon) {
568
- this.effectState.switchingIn = true;
557
+ // Cloud Nine does not activate when Skill Swapped or when Neutralizing Gas leaves the field
558
+ this.add('-ability', pokemon, 'Cloud Nine');
559
+ this.effect.onStart.call(this, pokemon);
569
560
  },
570
561
  onStart(pokemon) {
571
- // Cloud Nine does not activate when Skill Swapped or when Neutralizing Gas leaves the field
572
562
  pokemon.abilityState.ending = false; // Clear the ending flag
573
- if (this.effectState.switchingIn) {
574
- this.add('-ability', pokemon, 'Cloud Nine');
575
- this.effectState.switchingIn = false;
576
- }
577
563
  this.eachEvent('WeatherChange', this.effect);
578
564
  },
579
565
  onEnd(pokemon) {
@@ -627,10 +613,22 @@ export const Abilities = {
627
613
  num: 213,
628
614
  },
629
615
  commander: {
616
+ onAnySwitchInPriority: -2,
617
+ onAnySwitchIn() {
618
+ this.effect.onUpdate.call(this, this.effectState.target);
619
+ },
620
+ onStart(pokemon) {
621
+ this.effect.onUpdate.call(this, pokemon);
622
+ },
630
623
  onUpdate(pokemon) {
631
624
  if (this.gameType !== 'doubles')
632
625
  return;
626
+ // don't run between when a Pokemon switches in and the resulting onSwitchIn event
627
+ if (this.queue.peek()?.choice === 'runSwitch')
628
+ return;
633
629
  const ally = pokemon.allies()[0];
630
+ if (pokemon.switchFlag || ally?.switchFlag)
631
+ return;
634
632
  if (!ally || pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') {
635
633
  // Handle any edge cases
636
634
  if (pokemon.getVolatile('commanding'))
@@ -716,6 +714,7 @@ export const Abilities = {
716
714
  num: 212,
717
715
  },
718
716
  costar: {
717
+ onSwitchInPriority: -2,
719
718
  onStart(pokemon) {
720
719
  const ally = pokemon.allies()[0];
721
720
  if (!ally)
@@ -1098,12 +1097,8 @@ export const Abilities = {
1098
1097
  },
1099
1098
  drizzle: {
1100
1099
  onStart(source) {
1101
- for (const action of this.queue) {
1102
- if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'kyogre')
1103
- return;
1104
- if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal')
1105
- break;
1106
- }
1100
+ if (source.species.id === 'kyogre' && source.item === 'blueorb')
1101
+ return;
1107
1102
  this.field.setWeather('raindance');
1108
1103
  },
1109
1104
  flags: {},
@@ -1113,12 +1108,8 @@ export const Abilities = {
1113
1108
  },
1114
1109
  drought: {
1115
1110
  onStart(source) {
1116
- for (const action of this.queue) {
1117
- if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'groudon')
1118
- return;
1119
- if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal')
1120
- break;
1121
- }
1111
+ if (source.species.id === 'groudon' && source.item === 'redorb')
1112
+ return;
1122
1113
  this.field.setWeather('sunnyday');
1123
1114
  },
1124
1115
  flags: {},
@@ -1218,14 +1209,12 @@ export const Abilities = {
1218
1209
  },
1219
1210
  embodyaspectcornerstone: {
1220
1211
  onStart(pokemon) {
1221
- if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied) {
1222
- this.effectState.embodied = true;
1212
+ if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' &&
1213
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1214
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1223
1215
  this.boost({ def: 1 }, pokemon);
1224
1216
  }
1225
1217
  },
1226
- onSwitchIn() {
1227
- delete this.effectState.embodied;
1228
- },
1229
1218
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1230
1219
  name: "Embody Aspect (Cornerstone)",
1231
1220
  rating: 3.5,
@@ -1233,14 +1222,12 @@ export const Abilities = {
1233
1222
  },
1234
1223
  embodyaspecthearthflame: {
1235
1224
  onStart(pokemon) {
1236
- if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied) {
1237
- this.effectState.embodied = true;
1225
+ if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' &&
1226
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1227
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1238
1228
  this.boost({ atk: 1 }, pokemon);
1239
1229
  }
1240
1230
  },
1241
- onSwitchIn() {
1242
- delete this.effectState.embodied;
1243
- },
1244
1231
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1245
1232
  name: "Embody Aspect (Hearthflame)",
1246
1233
  rating: 3.5,
@@ -1248,14 +1235,12 @@ export const Abilities = {
1248
1235
  },
1249
1236
  embodyaspectteal: {
1250
1237
  onStart(pokemon) {
1251
- if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied) {
1252
- this.effectState.embodied = true;
1238
+ if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' &&
1239
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1240
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1253
1241
  this.boost({ spe: 1 }, pokemon);
1254
1242
  }
1255
1243
  },
1256
- onSwitchIn() {
1257
- delete this.effectState.embodied;
1258
- },
1259
1244
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1260
1245
  name: "Embody Aspect (Teal)",
1261
1246
  rating: 3.5,
@@ -1263,14 +1248,12 @@ export const Abilities = {
1263
1248
  },
1264
1249
  embodyaspectwellspring: {
1265
1250
  onStart(pokemon) {
1266
- if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied) {
1267
- this.effectState.embodied = true;
1251
+ if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' &&
1252
+ this.effectState.embodied !== pokemon.previouslySwitchedIn) {
1253
+ this.effectState.embodied = pokemon.previouslySwitchedIn;
1268
1254
  this.boost({ spd: 1 }, pokemon);
1269
1255
  }
1270
1256
  },
1271
- onSwitchIn() {
1272
- delete this.effectState.embodied;
1273
- },
1274
1257
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
1275
1258
  name: "Embody Aspect (Wellspring)",
1276
1259
  rating: 3.5,
@@ -1393,6 +1376,7 @@ export const Abilities = {
1393
1376
  num: 18,
1394
1377
  },
1395
1378
  flowergift: {
1379
+ onSwitchInPriority: -2,
1396
1380
  onStart(pokemon) {
1397
1381
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
1398
1382
  },
@@ -1488,6 +1472,7 @@ export const Abilities = {
1488
1472
  num: 218,
1489
1473
  },
1490
1474
  forecast: {
1475
+ onSwitchInPriority: -2,
1491
1476
  onStart(pokemon) {
1492
1477
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
1493
1478
  },
@@ -1913,6 +1898,7 @@ export const Abilities = {
1913
1898
  num: 118,
1914
1899
  },
1915
1900
  hospitality: {
1901
+ onSwitchInPriority: -2,
1916
1902
  onStart(pokemon) {
1917
1903
  for (const ally of pokemon.adjacentAllies()) {
1918
1904
  this.heal(ally.baseMaxhp / 4, ally, pokemon);
@@ -2010,6 +1996,7 @@ export const Abilities = {
2010
1996
  num: 115,
2011
1997
  },
2012
1998
  iceface: {
1999
+ onSwitchInPriority: -2,
2013
2000
  onStart(pokemon) {
2014
2001
  if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') {
2015
2002
  this.add('-activate', pokemon, 'ability: Ice Face');
@@ -2128,8 +2115,7 @@ export const Abilities = {
2128
2115
  if (pokemon.illusion) {
2129
2116
  this.debug('illusion cleared');
2130
2117
  pokemon.illusion = null;
2131
- const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
2132
- (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
2118
+ const details = pokemon.getUpdatedDetails();
2133
2119
  this.add('replace', pokemon, details);
2134
2120
  this.add('-end', pokemon, 'Illusion');
2135
2121
  if (this.ruleTable.has('illusionlevelmod')) {
@@ -2167,20 +2153,14 @@ export const Abilities = {
2167
2153
  },
2168
2154
  imposter: {
2169
2155
  onSwitchIn(pokemon) {
2170
- this.effectState.switchingIn = true;
2171
- },
2172
- onStart(pokemon) {
2173
2156
  // Imposter does not activate when Skill Swapped or when Neutralizing Gas leaves the field
2174
- if (!this.effectState.switchingIn)
2175
- return;
2176
- // copies across in doubles/triples
2157
+ // Imposter copies across in doubles/triples
2177
2158
  // (also copies across in multibattle and diagonally in free-for-all,
2178
2159
  // but side.foe already takes care of those)
2179
2160
  const target = pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
2180
2161
  if (target) {
2181
2162
  pokemon.transformInto(target, this.dex.abilities.get('imposter'));
2182
2163
  }
2183
- this.effectState.switchingIn = false;
2184
2164
  },
2185
2165
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
2186
2166
  name: "Imposter",
@@ -2339,6 +2319,9 @@ export const Abilities = {
2339
2319
  num: 51,
2340
2320
  },
2341
2321
  klutz: {
2322
+ // Klutz isn't technically active immediately in-game, but it activates early enough to beat all items
2323
+ // we should keep an eye out in future gens for items that activate on switch-in before Unnerve
2324
+ onSwitchInPriority: 1,
2342
2325
  // Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
2343
2326
  onStart(pokemon) {
2344
2327
  this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
@@ -2377,7 +2360,7 @@ export const Abilities = {
2377
2360
  },
2378
2361
  libero: {
2379
2362
  onPrepareHit(source, target, move) {
2380
- if (this.effectState.libero)
2363
+ if (this.effectState.libero === source.previouslySwitchedIn)
2381
2364
  return;
2382
2365
  if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove)
2383
2366
  return;
@@ -2385,13 +2368,10 @@ export const Abilities = {
2385
2368
  if (type && type !== '???' && source.getTypes().join() !== type) {
2386
2369
  if (!source.setType(type))
2387
2370
  return;
2388
- this.effectState.libero = true;
2371
+ this.effectState.libero = source.previouslySwitchedIn;
2389
2372
  this.add('-start', source, 'typechange', type, '[from] ability: Libero');
2390
2373
  }
2391
2374
  },
2392
- onSwitchIn() {
2393
- delete this.effectState.libero;
2394
- },
2395
2375
  flags: {},
2396
2376
  name: "Libero",
2397
2377
  rating: 4,
@@ -2642,6 +2622,7 @@ export const Abilities = {
2642
2622
  num: 196,
2643
2623
  },
2644
2624
  mimicry: {
2625
+ onSwitchInPriority: -1,
2645
2626
  onStart(pokemon) {
2646
2627
  this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon);
2647
2628
  },
@@ -2978,7 +2959,8 @@ export const Abilities = {
2978
2959
  },
2979
2960
  neutralizinggas: {
2980
2961
  // Ability suppression implemented in sim/pokemon.ts:Pokemon#ignoringAbility
2981
- onPreStart(pokemon) {
2962
+ onSwitchInPriority: 2,
2963
+ onSwitchIn(pokemon) {
2982
2964
  this.add('-ability', pokemon, 'Neutralizing Gas');
2983
2965
  pokemon.abilityState.ending = false;
2984
2966
  const strongWeathers = ['desolateland', 'primordialsea', 'deltastream'];
@@ -3119,17 +3101,38 @@ export const Abilities = {
3119
3101
  onFoeAfterBoost(boost, target, source, effect) {
3120
3102
  if (effect?.name === 'Opportunist' || effect?.name === 'Mirror Herb')
3121
3103
  return;
3122
- const pokemon = this.effectState.target;
3123
- const positiveBoosts = {};
3104
+ if (!this.effectState.boosts)
3105
+ this.effectState.boosts = {};
3106
+ const boostPlus = this.effectState.boosts;
3124
3107
  let i;
3125
3108
  for (i in boost) {
3126
3109
  if (boost[i] > 0) {
3127
- positiveBoosts[i] = boost[i];
3110
+ boostPlus[i] = (boostPlus[i] || 0) + boost[i];
3128
3111
  }
3129
3112
  }
3130
- if (Object.keys(positiveBoosts).length < 1)
3113
+ },
3114
+ onAnySwitchInPriority: -3,
3115
+ onAnySwitchIn() {
3116
+ if (!this.effectState.boosts)
3131
3117
  return;
3132
- this.boost(positiveBoosts, pokemon);
3118
+ this.boost(this.effectState.boosts, this.effectState.target);
3119
+ delete this.effectState.boosts;
3120
+ },
3121
+ onAnyAfterMove() {
3122
+ if (!this.effectState.boosts)
3123
+ return;
3124
+ this.boost(this.effectState.boosts, this.effectState.target);
3125
+ delete this.effectState.boosts;
3126
+ },
3127
+ onResidualOrder: 29,
3128
+ onResidual(pokemon) {
3129
+ if (!this.effectState.boosts)
3130
+ return;
3131
+ this.boost(this.effectState.boosts, this.effectState.target);
3132
+ delete this.effectState.boosts;
3133
+ },
3134
+ onEnd() {
3135
+ delete this.effectState.boosts;
3133
3136
  },
3134
3137
  flags: {},
3135
3138
  name: "Opportunist",
@@ -3256,11 +3259,8 @@ export const Abilities = {
3256
3259
  pokemon.cureStatus();
3257
3260
  }
3258
3261
  },
3259
- onAllySwitchIn(pokemon) {
3260
- if (['psn', 'tox'].includes(pokemon.status)) {
3261
- this.add('-activate', this.effectState.target, 'ability: Pastel Veil');
3262
- pokemon.cureStatus();
3263
- }
3262
+ onAnySwitchIn() {
3263
+ this.effect.onStart.call(this, this.effectState.target);
3264
3264
  },
3265
3265
  onSetStatus(status, target, source, effect) {
3266
3266
  if (!['psn', 'tox'].includes(status.id))
@@ -3567,7 +3567,7 @@ export const Abilities = {
3567
3567
  },
3568
3568
  protean: {
3569
3569
  onPrepareHit(source, target, move) {
3570
- if (this.effectState.protean)
3570
+ if (this.effectState.protean === source.previouslySwitchedIn)
3571
3571
  return;
3572
3572
  if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove)
3573
3573
  return;
@@ -3575,19 +3575,17 @@ export const Abilities = {
3575
3575
  if (type && type !== '???' && source.getTypes().join() !== type) {
3576
3576
  if (!source.setType(type))
3577
3577
  return;
3578
- this.effectState.protean = true;
3578
+ this.effectState.protean = source.previouslySwitchedIn;
3579
3579
  this.add('-start', source, 'typechange', type, '[from] ability: Protean');
3580
3580
  }
3581
3581
  },
3582
- onSwitchIn(pokemon) {
3583
- delete this.effectState.protean;
3584
- },
3585
3582
  flags: {},
3586
3583
  name: "Protean",
3587
3584
  rating: 4,
3588
3585
  num: 168,
3589
3586
  },
3590
3587
  protosynthesis: {
3588
+ onSwitchInPriority: -2,
3591
3589
  onStart(pokemon) {
3592
3590
  this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
3593
3591
  },
@@ -3596,8 +3594,7 @@ export const Abilities = {
3596
3594
  if (this.field.isWeather('sunnyday')) {
3597
3595
  pokemon.addVolatile('protosynthesis');
3598
3596
  }
3599
- else if (!pokemon.volatiles['protosynthesis']?.fromBooster && this.field.weather !== 'sunnyday') {
3600
- // Protosynthesis will not deactivite if Sun is suppressed, hence the direct ID check (isWeather respects supression)
3597
+ else if (!pokemon.volatiles['protosynthesis']?.fromBooster && !this.field.isWeather('sunnyday')) {
3601
3598
  pokemon.removeVolatile('protosynthesis');
3602
3599
  }
3603
3600
  },
@@ -3732,6 +3729,7 @@ export const Abilities = {
3732
3729
  num: 272,
3733
3730
  },
3734
3731
  quarkdrive: {
3732
+ onSwitchInPriority: -2,
3735
3733
  onStart(pokemon) {
3736
3734
  this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon);
3737
3735
  },
@@ -4147,6 +4145,7 @@ export const Abilities = {
4147
4145
  num: 157,
4148
4146
  },
4149
4147
  schooling: {
4148
+ onSwitchInPriority: -1,
4150
4149
  onStart(pokemon) {
4151
4150
  if (pokemon.baseSpecies.baseSpecies !== 'Wishiwashi' || pokemon.level < 20 || pokemon.transformed)
4152
4151
  return;
@@ -4350,6 +4349,7 @@ export const Abilities = {
4350
4349
  num: 19,
4351
4350
  },
4352
4351
  shieldsdown: {
4352
+ onSwitchInPriority: -1,
4353
4353
  onStart(pokemon) {
4354
4354
  if (pokemon.baseSpecies.baseSpecies !== 'Minior' || pokemon.transformed)
4355
4355
  return;
@@ -5092,21 +5092,7 @@ export const Abilities = {
5092
5092
  num: 309,
5093
5093
  },
5094
5094
  terashell: {
5095
- onEffectiveness(typeMod, target, type, move) {
5096
- if (!target || target.species.name !== 'Terapagos-Terastal')
5097
- return;
5098
- if (this.effectState.resisted)
5099
- return -1; // all hits of multi-hit move should be not very effective
5100
- if (move.category === 'Status' || move.id === 'struggle')
5101
- return;
5102
- if (!target.runImmunity(move.type))
5103
- return; // immunity has priority
5104
- if (target.hp < target.maxhp)
5105
- return;
5106
- this.add('-activate', target, 'ability: Tera Shell');
5107
- this.effectState.resisted = true;
5108
- return -1;
5109
- },
5095
+ // effectiveness implemented in sim/pokemon.ts:Pokemon#runEffectiveness
5110
5096
  onAnyAfterMove() {
5111
5097
  this.effectState.resisted = false;
5112
5098
  },
@@ -5116,12 +5102,14 @@ export const Abilities = {
5116
5102
  num: 308,
5117
5103
  },
5118
5104
  terashift: {
5119
- onPreStart(pokemon) {
5105
+ onSwitchInPriority: 2,
5106
+ onSwitchIn(pokemon) {
5120
5107
  if (pokemon.baseSpecies.baseSpecies !== 'Terapagos')
5121
5108
  return;
5122
5109
  if (pokemon.species.forme !== 'Terastal') {
5123
5110
  this.add('-activate', pokemon, 'ability: Tera Shift');
5124
5111
  pokemon.formeChange('Terapagos-Terastal', this.effect, true);
5112
+ pokemon.regressionForme = false;
5125
5113
  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);
5126
5114
  const newMaxHP = pokemon.baseMaxhp;
5127
5115
  pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
@@ -5277,19 +5265,23 @@ export const Abilities = {
5277
5265
  },
5278
5266
  trace: {
5279
5267
  onStart(pokemon) {
5268
+ this.effectState.seek = true;
5280
5269
  // n.b. only affects Hackmons
5281
5270
  // interaction with No Ability is complicated: https://www.smogon.com/forums/threads/pokemon-sun-moon-battle-mechanics-research.3586701/page-76#post-7790209
5282
5271
  if (pokemon.adjacentFoes().some(foeActive => foeActive.ability === 'noability')) {
5283
- this.effectState.gaveUp = true;
5272
+ this.effectState.seek = false;
5284
5273
  }
5285
5274
  // interaction with Ability Shield is similar to No Ability
5286
5275
  if (pokemon.hasItem('Ability Shield')) {
5287
5276
  this.add('-block', pokemon, 'item: Ability Shield');
5288
- this.effectState.gaveUp = true;
5277
+ this.effectState.seek = false;
5278
+ }
5279
+ if (this.effectState.seek) {
5280
+ this.singleEvent('Update', this.effect, this.effectState, pokemon);
5289
5281
  }
5290
5282
  },
5291
5283
  onUpdate(pokemon) {
5292
- if (!pokemon.isStarted || this.effectState.gaveUp)
5284
+ if (!this.effectState.seek)
5293
5285
  return;
5294
5286
  const possibleTargets = pokemon.adjacentFoes().filter(target => !target.getAbility().flags['notrace'] && target.ability !== 'noability');
5295
5287
  if (!possibleTargets.length)
@@ -5415,10 +5407,7 @@ export const Abilities = {
5415
5407
  num: 84,
5416
5408
  },
5417
5409
  unnerve: {
5418
- onPreStart(pokemon) {
5419
- this.add('-ability', pokemon, 'Unnerve');
5420
- this.effectState.unnerved = true;
5421
- },
5410
+ onSwitchInPriority: 1,
5422
5411
  onStart(pokemon) {
5423
5412
  if (this.effectState.unnerved)
5424
5413
  return;
@@ -5825,15 +5814,10 @@ export const Abilities = {
5825
5814
  return;
5826
5815
  if (pokemon.species.forme !== 'Hero') {
5827
5816
  pokemon.formeChange('Palafin-Hero', this.effect, true);
5817
+ pokemon.regressionForme = false;
5828
5818
  }
5829
5819
  },
5830
- onSwitchIn() {
5831
- this.effectState.switchingIn = true;
5832
- },
5833
- onStart(pokemon) {
5834
- if (!this.effectState.switchingIn)
5835
- return;
5836
- this.effectState.switchingIn = false;
5820
+ onSwitchIn(pokemon) {
5837
5821
  if (pokemon.baseSpecies.baseSpecies !== 'Palafin')
5838
5822
  return;
5839
5823
  if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === 'Hero') {