@pkmn/sim 0.6.1 → 0.6.2

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 (43) hide show
  1. package/build/config/formats.js +71 -97
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/aliases.js +2 -2
  4. package/build/data/aliases.js.map +1 -1
  5. package/build/data/formats-data.js +111 -115
  6. package/build/data/formats-data.js.map +1 -1
  7. package/build/data/items.js +7 -1
  8. package/build/data/items.js.map +1 -1
  9. package/build/data/learnsets.js +3 -2
  10. package/build/data/learnsets.js.map +1 -1
  11. package/build/data/mods/gen1/moves.js +0 -10
  12. package/build/data/mods/gen1/moves.js.map +1 -1
  13. package/build/data/mods/gen1/scripts.js +15 -14
  14. package/build/data/mods/gen1/scripts.js.map +1 -1
  15. package/build/data/mods/gen2/moves.js +0 -1
  16. package/build/data/mods/gen2/moves.js.map +1 -1
  17. package/build/data/moves.js +44 -25
  18. package/build/data/moves.js.map +1 -1
  19. package/build/data/pokedex.js +1 -1
  20. package/build/sim/dex-conditions.d.ts +1 -0
  21. package/build/sim/dex-conditions.js.map +1 -1
  22. package/build/sim/field.js +1 -0
  23. package/build/sim/field.js.map +1 -1
  24. package/build/sim/pokemon.js +2 -10
  25. package/build/sim/pokemon.js.map +1 -1
  26. package/build/sim/team-validator.d.ts +2 -2
  27. package/build/sim/team-validator.js +19 -14
  28. package/build/sim/team-validator.js.map +1 -1
  29. package/config/formats.ts +75 -103
  30. package/data/aliases.ts +2 -2
  31. package/data/formats-data.ts +111 -115
  32. package/data/items.ts +7 -1
  33. package/data/learnsets.ts +3 -2
  34. package/data/mods/gen1/moves.ts +0 -10
  35. package/data/mods/gen1/scripts.ts +14 -14
  36. package/data/mods/gen2/moves.ts +0 -1
  37. package/data/moves.ts +43 -23
  38. package/data/pokedex.ts +1 -1
  39. package/package.json +1 -1
  40. package/sim/dex-conditions.ts +1 -0
  41. package/sim/field.ts +1 -0
  42. package/sim/pokemon.ts +1 -8
  43. package/sim/team-validator.ts +27 -15
package/data/items.ts CHANGED
@@ -4576,7 +4576,13 @@ export const Items: {[itemid: string]: ItemData} = {
4576
4576
  fling: {
4577
4577
  basePower: 100,
4578
4578
  },
4579
- onUpdate(pokemon) {
4579
+ onStart(pokemon) {
4580
+ if (!pokemon.ignoringItem() && this.field.getPseudoWeather('trickroom')) {
4581
+ pokemon.useItem();
4582
+ }
4583
+ },
4584
+ onAnyPseudoWeatherStart() {
4585
+ const pokemon = this.effectState.target;
4580
4586
  if (this.field.getPseudoWeather('trickroom')) {
4581
4587
  pokemon.useItem();
4582
4588
  }
package/data/learnsets.ts CHANGED
@@ -79223,7 +79223,7 @@ export const Learnsets: {[speciesid: string]: LearnsetData} = {
79223
79223
  blizzard: ["8M"],
79224
79224
  bodyslam: ["8M"],
79225
79225
  boomburst: ["8L75"],
79226
- calmmind: ["8M", "8S0"],
79226
+ calmmind: ["8M"],
79227
79227
  charm: ["8M"],
79228
79228
  crunch: ["8M", "8L35"],
79229
79229
  darkpulse: ["8M", "8S0"],
@@ -79271,6 +79271,7 @@ export const Learnsets: {[speciesid: string]: LearnsetData} = {
79271
79271
  stompingtantrum: ["8M", "8L30"],
79272
79272
  substitute: ["8M"],
79273
79273
  sunnyday: ["8M"],
79274
+ switcheroo: ["8S0"],
79274
79275
  taunt: ["8M"],
79275
79276
  thief: ["8M"],
79276
79277
  thunder: ["8M"],
@@ -79283,7 +79284,7 @@ export const Learnsets: {[speciesid: string]: LearnsetData} = {
79283
79284
  wideguard: ["8L50"],
79284
79285
  },
79285
79286
  eventData: [
79286
- {generation: 8, level: 50, moves: ["recover", "calmmind", "darkpulse", "belch"], pokeball: "cherishball"},
79287
+ {generation: 8, level: 50, moves: ["recover", "switcheroo", "darkpulse", "belch"], pokeball: "cherishball"},
79287
79288
  ],
79288
79289
  },
79289
79290
  syclant: {
@@ -884,16 +884,6 @@ export const Moves: {[k: string]: ModdedMoveData} = {
884
884
  status: 'par',
885
885
  },
886
886
  },
887
- thunderwave: {
888
- inherit: true,
889
- accuracy: 100,
890
- onTryHit(target) {
891
- if (target.hasType('Ground')) {
892
- this.add('-immune', target);
893
- return null;
894
- }
895
- },
896
- },
897
887
  triattack: {
898
888
  inherit: true,
899
889
  onHit() {},
@@ -18,7 +18,7 @@ export const Scripts: ModdedBattleScriptsData = {
18
18
  getStat(statName, unmodified) {
19
19
  // @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid
20
20
  if (statName === 'hp') throw new Error("Please read `maxhp` directly");
21
- if (unmodified) return this.storedStats[statName];
21
+ if (unmodified) return this.baseStoredStats[statName];
22
22
  return this.modifiedStats![statName];
23
23
  },
24
24
  // Gen 1 function to apply a stat modification that is only active until the stat is recalculated or mon switched.
@@ -46,9 +46,7 @@ export const Scripts: ModdedBattleScriptsData = {
46
46
  changed = true;
47
47
  // Recalculate the modified stat
48
48
  if (i === 'evasion' || i === 'accuracy') continue;
49
- let stat = this.species.baseStats[i];
50
- stat = Math.floor(Math.floor(2 * stat + this.set.ivs[i] + Math.floor(this.set.evs[i] / 4)) * this.level / 100 + 5);
51
- this.modifiedStats![i] = this.storedStats[i] = Math.floor(stat);
49
+ this.modifiedStats![i] = this.storedStats[i];
52
50
  if (this.boosts[i] >= 0) {
53
51
  this.modifyStat!(i, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[i]]);
54
52
  } else {
@@ -63,9 +61,7 @@ export const Scripts: ModdedBattleScriptsData = {
63
61
  this.boosts[i] = 0;
64
62
  // Recalculate the modified stat
65
63
  if (i === 'evasion' || i === 'accuracy') continue;
66
- let stat = this.species.baseStats[i];
67
- stat = Math.floor(Math.floor(2 * stat + this.set.ivs[i] + Math.floor(this.set.evs[i] / 4)) * this.level / 100 + 5);
68
- this.modifiedStats![i] = this.storedStats[i] = Math.floor(stat);
64
+ this.modifiedStats![i] = this.storedStats[i];
69
65
  }
70
66
  },
71
67
  },
@@ -334,6 +330,7 @@ export const Scripts: ModdedBattleScriptsData = {
334
330
  let i: number;
335
331
  for (i = 0; i < hits && target.hp && pokemon.hp; i++) {
336
332
  move.hit = i + 1;
333
+ if (move.hit === hits) move.lastHit = true;
337
334
  moveDamage = this.moveHit(target, pokemon, move);
338
335
  if (moveDamage === false) break;
339
336
  damage = (moveDamage || 0);
@@ -573,13 +570,16 @@ export const Scripts: ModdedBattleScriptsData = {
573
570
  // Apply move secondaries.
574
571
  if (moveData.secondaries) {
575
572
  for (const secondary of moveData.secondaries) {
576
- // We check here whether to negate the probable secondary status if it's para, burn, or freeze.
577
- // In the game, this is checked and if true, the random number generator is not called.
578
- // That means that a move that does not share the type of the target can status it.
579
- // If a move that was not fire-type would exist on Gen 1, it could burn a Pokémon.
580
- if (!(secondary.status && ['par', 'brn', 'frz'].includes(secondary.status) && target && target.hasType(move.type))) {
581
- if (secondary.chance === undefined || this.battle.randomChance(Math.ceil(secondary.chance * 256 / 100), 256)) {
582
- this.moveHit(target, pokemon, move, secondary, true, isSelf);
573
+ // Multi-hit moves only roll for status once
574
+ if (!move.multihit || move.lastHit) {
575
+ // We check here whether to negate the probable secondary status if it's para, burn, or freeze.
576
+ // In the game, this is checked and if true, the random number generator is not called.
577
+ // That means that a move that does not share the type of the target can status it.
578
+ // If a move that was not fire-type would exist on Gen 1, it could burn a Pokémon.
579
+ if (!(secondary.status && ['par', 'brn', 'frz'].includes(secondary.status) && target && target.hasType(move.type))) {
580
+ if (secondary.chance === undefined || this.battle.randomChance(Math.ceil(secondary.chance * 256 / 100), 256)) {
581
+ this.moveHit(target, pokemon, move, secondary, true, isSelf);
582
+ }
583
583
  }
584
584
  }
585
585
  }
@@ -415,7 +415,6 @@ export const Moves: {[k: string]: ModdedMoveData} = {
415
415
  },
416
416
  mimic: {
417
417
  inherit: true,
418
- accuracy: true,
419
418
  ignoreAccuracy: undefined,
420
419
  ignoreEvasion: undefined,
421
420
  noSketch: true,
package/data/moves.ts CHANGED
@@ -8707,10 +8707,16 @@ export const Moves: {[moveid: string]: MoveData} = {
8707
8707
  basePower: 30,
8708
8708
  basePowerCallback(pokemon, target, move) {
8709
8709
  let bp = move.basePower;
8710
- if (pokemon.volatiles['iceball'] && pokemon.volatiles['iceball'].hitCount) {
8711
- bp *= Math.pow(2, pokemon.volatiles['iceball'].hitCount);
8710
+ const iceballData = pokemon.volatiles['iceball'];
8711
+ if (iceballData?.hitCount) {
8712
+ bp *= Math.pow(2, iceballData.hitCount);
8713
+ }
8714
+ if (iceballData && pokemon.status !== 'slp') {
8715
+ iceballData.hitCount++;
8716
+ if (iceballData.hitCount < 5) {
8717
+ iceballData.duration = 2;
8718
+ }
8712
8719
  }
8713
- if (pokemon.status !== 'slp') pokemon.addVolatile('iceball');
8714
8720
  if (pokemon.volatiles['defensecurl']) {
8715
8721
  bp *= 2;
8716
8722
  }
@@ -8723,17 +8729,19 @@ export const Moves: {[moveid: string]: MoveData} = {
8723
8729
  pp: 20,
8724
8730
  priority: 0,
8725
8731
  flags: {bullet: 1, contact: 1, protect: 1, mirror: 1},
8732
+ onModifyMove(move, pokemon, target) {
8733
+ if (pokemon.volatiles['iceball'] || pokemon.status === 'slp' || !target) return;
8734
+ pokemon.addVolatile('iceball');
8735
+ // @ts-ignore
8736
+ // TS thinks pokemon.volatiles['iceball'] doesn't exist because of the condition on the return above
8737
+ // but it does exist now because addVolatile created it
8738
+ pokemon.volatiles['iceball'].targetSlot = move.sourceEffect ? pokemon.lastMoveTargetLoc : pokemon.getLocOf(target);
8739
+ },
8726
8740
  condition: {
8727
- duration: 2,
8741
+ duration: 1,
8728
8742
  onLockMove: 'iceball',
8729
8743
  onStart() {
8730
- this.effectState.hitCount = 1;
8731
- },
8732
- onRestart() {
8733
- this.effectState.hitCount++;
8734
- if (this.effectState.hitCount < 5) {
8735
- this.effectState.duration = 2;
8736
- }
8744
+ this.effectState.hitCount = 0;
8737
8745
  },
8738
8746
  onResidual(target) {
8739
8747
  if (target.lastMove && target.lastMove.id === 'struggle') {
@@ -13434,7 +13442,11 @@ export const Moves: {[moveid: string]: MoveData} = {
13434
13442
  priority: 0,
13435
13443
  flags: {protect: 1, reflectable: 1, heal: 1},
13436
13444
  onHit(target, source) {
13437
- if (!target.cureStatus()) return this.NOT_FAIL;
13445
+ if (!target.cureStatus()) {
13446
+ this.add('-fail', source);
13447
+ this.attrLastMove('[still]');
13448
+ return this.NOT_FAIL;
13449
+ }
13438
13450
  this.heal(Math.ceil(source.maxhp * 0.5), source);
13439
13451
  },
13440
13452
  secondary: null,
@@ -14390,10 +14402,16 @@ export const Moves: {[moveid: string]: MoveData} = {
14390
14402
  basePower: 30,
14391
14403
  basePowerCallback(pokemon, target, move) {
14392
14404
  let bp = move.basePower;
14393
- if (pokemon.volatiles['rollout'] && pokemon.volatiles['rollout'].hitCount) {
14394
- bp *= Math.pow(2, pokemon.volatiles['rollout'].hitCount);
14405
+ const rolloutData = pokemon.volatiles['rollout'];
14406
+ if (rolloutData?.hitCount) {
14407
+ bp *= Math.pow(2, rolloutData.hitCount);
14408
+ }
14409
+ if (rolloutData && pokemon.status !== 'slp') {
14410
+ rolloutData.hitCount++;
14411
+ if (rolloutData.hitCount < 5) {
14412
+ rolloutData.duration = 2;
14413
+ }
14395
14414
  }
14396
- if (pokemon.status !== 'slp') pokemon.addVolatile('rollout');
14397
14415
  if (pokemon.volatiles['defensecurl']) {
14398
14416
  bp *= 2;
14399
14417
  }
@@ -14405,17 +14423,19 @@ export const Moves: {[moveid: string]: MoveData} = {
14405
14423
  pp: 20,
14406
14424
  priority: 0,
14407
14425
  flags: {contact: 1, protect: 1, mirror: 1},
14426
+ onModifyMove(move, pokemon, target) {
14427
+ if (pokemon.volatiles['rollout'] || pokemon.status === 'slp' || !target) return;
14428
+ pokemon.addVolatile('rollout');
14429
+ // @ts-ignore
14430
+ // TS thinks pokemon.volatiles['rollout'] doesn't exist because of the condition on the return above
14431
+ // but it does exist now because addVolatile created it
14432
+ pokemon.volatiles['rollout'].targetSlot = move.sourceEffect ? pokemon.lastMoveTargetLoc : pokemon.getLocOf(target);
14433
+ },
14408
14434
  condition: {
14409
- duration: 2,
14435
+ duration: 1,
14410
14436
  onLockMove: 'rollout',
14411
14437
  onStart() {
14412
- this.effectState.hitCount = 1;
14413
- },
14414
- onRestart() {
14415
- this.effectState.hitCount++;
14416
- if (this.effectState.hitCount < 5) {
14417
- this.effectState.duration = 2;
14418
- }
14438
+ this.effectState.hitCount = 0;
14419
14439
  },
14420
14440
  onResidual(target) {
14421
14441
  if (target.lastMove && target.lastMove.id === 'struggle') {
package/data/pokedex.ts CHANGED
@@ -17309,7 +17309,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
17309
17309
  chromera: {
17310
17310
  num: -60,
17311
17311
  name: "Chromera",
17312
- types: ["Dark", "Poison"],
17312
+ types: ["Dark", "Normal"],
17313
17313
  gender: "N",
17314
17314
  baseStats: {hp: 85, atk: 85, def: 115, spa: 115, spd: 100, spe: 100},
17315
17315
  abilities: {0: "Color Change"},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown",
5
5
  "homepage": "https://psim.us",
6
6
  "main": "build/sim/index.js",
@@ -390,6 +390,7 @@ export interface EventMethods {
390
390
  onAnyNegateImmunity?: ((this: Battle, pokemon: Pokemon, type: string) => boolean | void) | boolean;
391
391
  onAnyOverrideAction?: (this: Battle, pokemon: Pokemon, target: Pokemon, move: ActiveMove) => string | void;
392
392
  onAnyPrepareHit?: CommonHandlers['ResultSourceMove'];
393
+ onAnyPseudoWeatherStart?: (this: Battle, target: Pokemon, source: Pokemon, pseudoWeather: Condition) => void;
393
394
  onAnyRedirectTarget?: (
394
395
  this: Battle, target: Pokemon, source: Pokemon, source2: Effect, move: ActiveMove
395
396
  ) => Pokemon | void;
package/sim/field.ts CHANGED
@@ -218,6 +218,7 @@ export class Field {
218
218
  delete this.pseudoWeather[status.id];
219
219
  return false;
220
220
  }
221
+ this.battle.runEvent('PseudoWeatherStart', source, source, status);
221
222
  return true;
222
223
  }
223
224
 
package/sim/pokemon.ts CHANGED
@@ -1180,6 +1180,7 @@ export class Pokemon {
1180
1180
  let statName: StatIDExceptHP;
1181
1181
  for (statName in this.storedStats) {
1182
1182
  this.storedStats[statName] = pokemon.storedStats[statName];
1183
+ if (this.modifiedStats) this.modifiedStats[statName] = pokemon.modifiedStats![statName]; // Gen 1: Copy modified stats.
1183
1184
  }
1184
1185
  this.moveSlots = [];
1185
1186
  this.set.ivs = (this.battle.gen >= 5 ? this.set.ivs : pokemon.set.ivs);
@@ -1204,14 +1205,6 @@ export class Pokemon {
1204
1205
  let boostName: BoostID;
1205
1206
  for (boostName in pokemon.boosts) {
1206
1207
  this.boosts[boostName] = pokemon.boosts[boostName];
1207
- if (this.battle.gen <= 1) {
1208
- if (boostName === 'evasion' || boostName === 'accuracy') continue;
1209
- if (this.boosts[boostName] >= 0) {
1210
- this.modifyStat!(boostName, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[boostName]]);
1211
- } else {
1212
- this.modifyStat!(boostName, [100, 66, 50, 40, 33, 28, 25][-this.boosts[boostName]] / 100);
1213
- }
1214
- }
1215
1208
  }
1216
1209
  if (this.battle.gen >= 6) {
1217
1210
  const volatilesToCopy = ['focusenergy', 'gmaxchistrike', 'laserfocus'];
@@ -687,7 +687,7 @@ export class TeamValidator {
687
687
  const {species: eventSpecies, eventData} = eventOnlyData;
688
688
  let legal = false;
689
689
  for (const event of eventData) {
690
- if (this.validateEvent(set, event, eventSpecies)) continue;
690
+ if (this.validateEvent(set, setSources, event, eventSpecies)) continue;
691
691
  legal = true;
692
692
  break;
693
693
  }
@@ -698,20 +698,19 @@ export class TeamValidator {
698
698
  if (eventData.length === 1) {
699
699
  problems.push(`${species.name} is only obtainable from an event - it needs to match its event:`);
700
700
  } else {
701
- problems.push(`${species.name} is only obtainable from events - it needs to match one of its events, such as:`);
701
+ problems.push(`${species.name} is only obtainable from events - it needs to match one of its events:`);
702
702
  }
703
- let eventInfo = eventData[0];
704
- let eventNum = 1;
705
703
  for (const [i, event] of eventData.entries()) {
706
704
  if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
707
- eventInfo = event;
708
- eventNum = i + 1;
709
- break;
705
+ const eventInfo = event;
706
+ const eventNum = i + 1;
707
+ const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
708
+ const eventProblems = this.validateEvent(
709
+ set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`
710
+ );
711
+ if (eventProblems) problems.push(...eventProblems);
710
712
  }
711
713
  }
712
- const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
713
- const eventProblems = this.validateEvent(set, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
714
- if (eventProblems) problems.push(...eventProblems);
715
714
  }
716
715
  }
717
716
 
@@ -1102,7 +1101,7 @@ export class TeamValidator {
1102
1101
  }
1103
1102
 
1104
1103
  // complicated fancy return signature
1105
- return this.validateEvent(set, eventData, eventSpecies, because as any) as any;
1104
+ return this.validateEvent(set, setSources, eventData, eventSpecies, because as any) as any;
1106
1105
  }
1107
1106
 
1108
1107
  findEggMoveFathers(source: PokemonSource, species: Species, setSources: PokemonSources): boolean;
@@ -1674,16 +1673,22 @@ export class TeamValidator {
1674
1673
  return null;
1675
1674
  }
1676
1675
 
1677
- validateEvent(set: PokemonSet, eventData: EventInfo, eventSpecies: Species): true | undefined;
1678
1676
  validateEvent(
1679
- set: PokemonSet, eventData: EventInfo, eventSpecies: Species, because: string, from?: string
1677
+ set: PokemonSet, setSources: PokemonSources, eventData: EventInfo, eventSpecies: Species
1678
+ ): true | undefined;
1679
+ validateEvent(
1680
+ set: PokemonSet, setSources: PokemonSources, eventData: EventInfo, eventSpecies: Species,
1681
+ because: string, from?: string
1680
1682
  ): string[] | undefined;
1681
1683
  /**
1682
1684
  * Returns array of error messages if invalid, undefined if valid
1683
1685
  *
1684
1686
  * If `because` is not passed, instead returns true if invalid.
1685
1687
  */
1686
- validateEvent(set: PokemonSet, eventData: EventInfo, eventSpecies: Species, because = ``, from = `from an event`) {
1688
+ validateEvent(
1689
+ set: PokemonSet, setSources: PokemonSources, eventData: EventInfo, eventSpecies: Species,
1690
+ because = ``, from = `from an event`
1691
+ ) {
1687
1692
  const dex = this.dex;
1688
1693
  let name = set.species;
1689
1694
  const species = dex.species.get(set.species);
@@ -1784,8 +1789,15 @@ export class TeamValidator {
1784
1789
  problems.push(`${name} can only use Hidden Power Dark/Dragon/Electric/Steel/Ice because it must have at least 5 perfect IVs${etc}.`);
1785
1790
  }
1786
1791
  }
1787
- // Event-related ability restrictions only matter if we care about illegal abilities
1788
1792
  const ruleTable = this.ruleTable;
1793
+ if (ruleTable.has('obtainablemoves')) {
1794
+ const ssMaxSourceGen = setSources.maxSourceGen();
1795
+ const tradebackEligible = dex.gen === 2 && species.gen === 1;
1796
+ if (ssMaxSourceGen && eventData.generation > ssMaxSourceGen && !tradebackEligible) {
1797
+ if (fastReturn) return true;
1798
+ problems.push(`${name} must not have moves only learnable in gen ${ssMaxSourceGen}${etc}.`);
1799
+ }
1800
+ }
1789
1801
  if (ruleTable.has('obtainableabilities')) {
1790
1802
  if (dex.gen <= 5 && eventData.abilities && eventData.abilities.length === 1 && !eventData.isHidden) {
1791
1803
  if (species.name === eventSpecies.name) {