@pkmn/sim 0.4.20 → 0.4.24

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 (88) hide show
  1. package/build/config/formats.js +415 -688
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/aliases.js +7 -5
  4. package/build/data/aliases.js.map +1 -1
  5. package/build/data/conditions.js +4 -1
  6. package/build/data/conditions.js.map +1 -1
  7. package/build/data/formats-data.js +18 -18
  8. package/build/data/formats-data.js.map +1 -1
  9. package/build/data/learnsets.js +10 -8
  10. package/build/data/learnsets.js.map +1 -1
  11. package/build/data/mods/gen1/moves.js +1 -1
  12. package/build/data/mods/gen1/moves.js.map +1 -1
  13. package/build/data/mods/gen1/scripts.js +18 -22
  14. package/build/data/mods/gen1/scripts.js.map +1 -1
  15. package/build/data/mods/gen2/scripts.js +16 -23
  16. package/build/data/mods/gen2/scripts.js.map +1 -1
  17. package/build/data/mods/gen3/moves.js +2 -1
  18. package/build/data/mods/gen3/moves.js.map +1 -1
  19. package/build/data/mods/gen4/conditions.js +6 -0
  20. package/build/data/mods/gen4/conditions.js.map +1 -1
  21. package/build/data/mods/gen4/moves.js +2 -1
  22. package/build/data/mods/gen4/moves.js.map +1 -1
  23. package/build/data/mods/gen4/scripts.js +2 -1
  24. package/build/data/mods/gen4/scripts.js.map +1 -1
  25. package/build/data/mods/gen6/conditions.js +4 -1
  26. package/build/data/mods/gen6/conditions.js.map +1 -1
  27. package/build/data/mods/gen7/formats-data.js +2 -2
  28. package/build/data/mods/gen7/formats-data.js.map +1 -1
  29. package/build/data/mods/gen7/moves.js +8 -0
  30. package/build/data/mods/gen7/moves.js.map +1 -1
  31. package/build/data/moves.js +25 -13
  32. package/build/data/moves.js.map +1 -1
  33. package/build/data/rulesets.js +221 -1
  34. package/build/data/rulesets.js.map +1 -1
  35. package/build/data/tags.js +2 -2
  36. package/build/data/tags.js.map +1 -1
  37. package/build/lib/streams.d.ts +1 -199
  38. package/build/lib/streams.js +11 -772
  39. package/build/lib/streams.js.map +1 -1
  40. package/build/sim/battle-actions.d.ts +1 -1
  41. package/build/sim/battle-actions.js +18 -42
  42. package/build/sim/battle-actions.js.map +1 -1
  43. package/build/sim/battle.d.ts +1 -0
  44. package/build/sim/battle.js +5 -0
  45. package/build/sim/battle.js.map +1 -1
  46. package/build/sim/dex-moves.d.ts +31 -11
  47. package/build/sim/dex-moves.js +4 -3
  48. package/build/sim/dex-moves.js.map +1 -1
  49. package/build/sim/dex-species.js +11 -1
  50. package/build/sim/dex-species.js.map +1 -1
  51. package/build/sim/exported-global-types.d.ts +1 -0
  52. package/build/sim/global-types.d.ts +1 -0
  53. package/build/sim/side.js +2 -2
  54. package/build/sim/side.js.map +1 -1
  55. package/build/sim/team-validator.js +2 -2
  56. package/build/sim/team-validator.js.map +1 -1
  57. package/build/sim/tools/runner.d.ts +1 -6
  58. package/build/sim/tools/runner.js +6 -6
  59. package/build/sim/tools/runner.js.map +1 -1
  60. package/config/formats.ts +393 -643
  61. package/data/aliases.ts +7 -5
  62. package/data/conditions.ts +4 -1
  63. package/data/formats-data.ts +18 -18
  64. package/data/learnsets.ts +10 -8
  65. package/data/mods/gen1/moves.ts +1 -1
  66. package/data/mods/gen1/scripts.ts +23 -19
  67. package/data/mods/gen2/scripts.ts +20 -19
  68. package/data/mods/gen3/moves.ts +2 -1
  69. package/data/mods/gen4/conditions.ts +6 -0
  70. package/data/mods/gen4/moves.ts +2 -1
  71. package/data/mods/gen4/scripts.ts +1 -1
  72. package/data/mods/gen6/conditions.ts +4 -1
  73. package/data/mods/gen7/formats-data.ts +2 -2
  74. package/data/mods/gen7/moves.ts +8 -0
  75. package/data/moves.ts +22 -13
  76. package/data/rulesets.ts +199 -1
  77. package/data/tags.ts +2 -2
  78. package/lib/streams.ts +1 -874
  79. package/package.json +3 -2
  80. package/sim/battle-actions.ts +19 -40
  81. package/sim/battle.ts +6 -0
  82. package/sim/dex-moves.ts +35 -13
  83. package/sim/dex-species.ts +10 -1
  84. package/sim/exported-global-types.ts +1 -0
  85. package/sim/global-types.ts +1 -0
  86. package/sim/side.ts +2 -2
  87. package/sim/team-validator.ts +2 -2
  88. package/sim/tools/runner.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.4.20",
3
+ "version": "0.4.24",
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",
@@ -27,7 +27,8 @@
27
27
  "sim"
28
28
  ],
29
29
  "dependencies": {
30
- "@pkmn/sets": "^2.0.0"
30
+ "@pkmn/sets": "^2.0.0",
31
+ "@pkmn/streams": "^1.0.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "mocha": "^9.1.3"
@@ -1505,7 +1505,7 @@ export class BattleActions {
1505
1505
  * undefined = success, null = silent failure, false = loud failure
1506
1506
  */
1507
1507
  getDamage(
1508
- pokemon: Pokemon, target: Pokemon, move: string | number | ActiveMove,
1508
+ source: Pokemon, target: Pokemon, move: string | number | ActiveMove,
1509
1509
  suppressMessages = false
1510
1510
  ): number | undefined | null | false {
1511
1511
  if (typeof move === 'string') move = this.dex.getActiveMove(move);
@@ -1528,25 +1528,24 @@ export class BattleActions {
1528
1528
  }
1529
1529
 
1530
1530
  if (move.ohko) return target.maxhp;
1531
- if (move.damageCallback) return move.damageCallback.call(this.battle, pokemon, target);
1531
+ if (move.damageCallback) return move.damageCallback.call(this.battle, source, target);
1532
1532
  if (move.damage === 'level') {
1533
- return pokemon.level;
1533
+ return source.level;
1534
1534
  } else if (move.damage) {
1535
1535
  return move.damage;
1536
1536
  }
1537
1537
 
1538
1538
  const category = this.battle.getCategory(move);
1539
- const defensiveCategory = move.defensiveCategory || category;
1540
1539
 
1541
1540
  let basePower: number | false | null = move.basePower;
1542
1541
  if (move.basePowerCallback) {
1543
- basePower = move.basePowerCallback.call(this.battle, pokemon, target, move);
1542
+ basePower = move.basePowerCallback.call(this.battle, source, target, move);
1544
1543
  }
1545
1544
  if (!basePower) return basePower === 0 ? undefined : basePower;
1546
1545
  basePower = this.battle.clampIntRange(basePower, 1);
1547
1546
 
1548
1547
  let critMult;
1549
- let critRatio = this.battle.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0);
1548
+ let critRatio = this.battle.runEvent('ModifyCritRatio', source, target, move, move.critRatio || 0);
1550
1549
  if (this.battle.gen <= 5) {
1551
1550
  critRatio = this.battle.clampIntRange(critRatio, 0, 5);
1552
1551
  critMult = [0, 16, 8, 4, 3, 2];
@@ -1572,39 +1571,23 @@ export class BattleActions {
1572
1571
  }
1573
1572
 
1574
1573
  // happens after crit calculation
1575
- basePower = this.battle.runEvent('BasePower', pokemon, target, move, basePower, true);
1574
+ basePower = this.battle.runEvent('BasePower', source, target, move, basePower, true);
1576
1575
 
1577
1576
  if (!basePower) return 0;
1578
1577
  basePower = this.battle.clampIntRange(basePower, 1);
1579
1578
 
1580
- const level = pokemon.level;
1579
+ const level = source.level;
1581
1580
 
1582
- const attacker = pokemon;
1583
- const defender = target;
1584
- let attackStat: StatIDExceptHP = category === 'Physical' ? 'atk' : 'spa';
1585
- const defenseStat: StatIDExceptHP = defensiveCategory === 'Physical' ? 'def' : 'spd';
1586
- if (move.useSourceDefensiveAsOffensive) {
1587
- attackStat = defenseStat;
1588
- // Body press really wants to use the def stat,
1589
- // so it switches stats to compensate for Wonder Room.
1590
- // Of course, the game thus miscalculates the boosts...
1591
- if ('wonderroom' in this.battle.field.pseudoWeather) {
1592
- if (attackStat === 'def') {
1593
- attackStat = 'spd';
1594
- } else if (attackStat === 'spd') {
1595
- attackStat = 'def';
1596
- }
1597
- if (attacker.boosts['def'] || attacker.boosts['spd']) {
1598
- this.battle.hint("Body Press uses Sp. Def boosts when Wonder Room is active.");
1599
- }
1600
- }
1601
- }
1581
+ const attacker = move.overrideOffensivePokemon === 'target' ? target : source;
1582
+ const defender = move.overrideDefensivePokemon === 'source' ? source : target;
1583
+
1584
+ const isPhysical = move.category === 'Physical';
1585
+ let attackStat: StatIDExceptHP = move.overrideOffensiveStat || (isPhysical ? 'atk' : 'spa');
1586
+ const defenseStat: StatIDExceptHP = move.overrideDefensiveStat || (isPhysical ? 'def' : 'spd');
1602
1587
 
1603
1588
  const statTable = {atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe'};
1604
- let attack;
1605
- let defense;
1606
1589
 
1607
- let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat];
1590
+ let atkBoosts = attacker.boosts[attackStat];
1608
1591
  let defBoosts = defender.boosts[defenseStat];
1609
1592
 
1610
1593
  let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive;
@@ -1626,18 +1609,14 @@ export class BattleActions {
1626
1609
  defBoosts = 0;
1627
1610
  }
1628
1611
 
1629
- if (move.useTargetOffensive) {
1630
- attack = defender.calculateStat(attackStat, atkBoosts);
1631
- } else {
1632
- attack = attacker.calculateStat(attackStat, atkBoosts);
1633
- }
1612
+ let attack = attacker.calculateStat(attackStat, atkBoosts);
1613
+ let defense = defender.calculateStat(defenseStat, defBoosts);
1634
1614
 
1635
1615
  attackStat = (category === 'Physical' ? 'atk' : 'spa');
1636
- defense = defender.calculateStat(defenseStat, defBoosts);
1637
1616
 
1638
1617
  // Apply Stat Modifiers
1639
- attack = this.battle.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack);
1640
- defense = this.battle.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense);
1618
+ attack = this.battle.runEvent('Modify' + statTable[attackStat], source, target, move, attack);
1619
+ defense = this.battle.runEvent('Modify' + statTable[defenseStat], target, source, move, defense);
1641
1620
 
1642
1621
  if (this.battle.gen <= 4 && ['explosion', 'selfdestruct'].includes(move.id) && defenseStat === 'def') {
1643
1622
  defense = this.battle.clampIntRange(Math.floor(defense / 2), 1);
@@ -1649,7 +1628,7 @@ export class BattleActions {
1649
1628
  const baseDamage = tr(tr(tr(tr(2 * level / 5 + 2) * basePower * attack) / defense) / 50);
1650
1629
 
1651
1630
  // Calculate damage modifiers separately (order differs between generations)
1652
- return this.modifyDamage(baseDamage, pokemon, target, move, suppressMessages);
1631
+ return this.modifyDamage(baseDamage, source, target, move, suppressMessages);
1653
1632
  }
1654
1633
 
1655
1634
  modifyDamage(
package/sim/battle.ts CHANGED
@@ -2102,6 +2102,12 @@ export class Battle {
2102
2102
  return stats;
2103
2103
  }
2104
2104
 
2105
+ finalModify(relayVar: number) {
2106
+ relayVar = this.modify(relayVar, this.event.modifier);
2107
+ this.event.modifier = 1;
2108
+ return relayVar;
2109
+ }
2110
+
2105
2111
  getCategory(move: string | Move): Move['category'] {
2106
2112
  return this.dex.moves.get(move).category || 'Physical';
2107
2113
  }
package/sim/dex-moves.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  Pokemon,
12
12
  Side,
13
13
  SparseBoostsTable,
14
+ StatIDExceptHP,
14
15
  } from './exported-global-types';
15
16
 
16
17
  import {Utils} from '../lib';
@@ -218,7 +219,22 @@ export interface MoveData extends EffectData, MoveEventMethods, HitEffect {
218
219
  basePowerModifier?: number;
219
220
  critModifier?: number;
220
221
  critRatio?: number;
221
- defensiveCategory?: 'Physical' | 'Special' | 'Status';
222
+ /**
223
+ * Pokemon for the attack stat. Ability and Item damage modifiers still come from the real attacker.
224
+ */
225
+ overrideOffensivePokemon?: 'target' | 'source';
226
+ /**
227
+ * Physical moves use attack stat modifiers, special moves use special attack stat modifiers.
228
+ */
229
+ overrideOffensiveStat?: StatIDExceptHP;
230
+ /**
231
+ * Pokemon for the defense stat. Ability and Item damage modifiers still come from the real defender.
232
+ */
233
+ overrideDefensivePokemon?: 'target' | 'source';
234
+ /**
235
+ * uses modifiers that match the new stat
236
+ */
237
+ overrideDefensiveStat?: StatIDExceptHP;
222
238
  forceSTAB?: boolean;
223
239
  ignoreAbility?: boolean;
224
240
  ignoreAccuracy?: boolean;
@@ -246,8 +262,6 @@ export interface MoveData extends EffectData, MoveEventMethods, HitEffect {
246
262
  * situations, rather than just targeting a slot. (Stalwart, Snipe Shot)
247
263
  */
248
264
  tracksTarget?: boolean;
249
- useTargetOffensive?: boolean;
250
- useSourceDefensiveAsOffensive?: boolean;
251
265
  willCrit?: boolean;
252
266
 
253
267
  // Mechanics flags
@@ -375,14 +389,21 @@ export class DataMove extends BasicEffect implements Readonly<BasicEffect & Move
375
389
  /** Move category. */
376
390
  readonly category: MoveCategory;
377
391
  /**
378
- * Category that changes which defense to use when calculating
379
- * move damage.
392
+ * Pokemon for the attack stat. Ability and Item damage modifiers still come from the real attacker.
393
+ */
394
+ readonly overrideOffensivePokemon?: 'target' | 'source';
395
+ /**
396
+ * Physical moves use attack stat modifiers, special moves use special attack stat modifiers.
397
+ */
398
+ readonly overrideOffensiveStat?: StatIDExceptHP;
399
+ /**
400
+ * Pokemon for the defense stat. Ability and Item damage modifiers still come from the real defender.
401
+ */
402
+ readonly overrideDefensivePokemon?: 'target' | 'source';
403
+ /**
404
+ * uses modifiers that match the new stat
380
405
  */
381
- readonly defensiveCategory?: MoveCategory;
382
- /** Uses the target's Atk/SpA as the attacking stat, instead of the user's. */
383
- readonly useTargetOffensive: boolean;
384
- /** Use the user's Def/SpD as the attacking stat, instead of Atk/SpA. */
385
- readonly useSourceDefensiveAsOffensive: boolean;
406
+ readonly overrideDefensiveStat?: StatIDExceptHP;
386
407
  /** Whether or not this move ignores negative attack boosts. */
387
408
  readonly ignoreNegativeOffensive: boolean;
388
409
  /** Whether or not this move ignores positive defense boosts. */
@@ -463,9 +484,10 @@ export class DataMove extends BasicEffect implements Readonly<BasicEffect & Move
463
484
  this.secondaries = data.secondaries || (this.secondary && [this.secondary]) || null;
464
485
  this.priority = Number(data.priority) || 0;
465
486
  this.category = data.category!;
466
- this.defensiveCategory = data.defensiveCategory || undefined;
467
- this.useTargetOffensive = !!data.useTargetOffensive;
468
- this.useSourceDefensiveAsOffensive = !!data.useSourceDefensiveAsOffensive;
487
+ this.overrideOffensiveStat = data.overrideOffensiveStat || undefined;
488
+ this.overrideOffensivePokemon = data.overrideOffensivePokemon || undefined;
489
+ this.overrideDefensiveStat = data.overrideDefensiveStat || undefined;
490
+ this.overrideDefensivePokemon = data.overrideDefensivePokemon || undefined;
469
491
  this.ignoreNegativeOffensive = !!data.ignoreNegativeOffensive;
470
492
  this.ignorePositiveDefensive = !!data.ignorePositiveDefensive;
471
493
  this.ignoreOffensive = !!data.ignoreOffensive;
@@ -479,15 +479,24 @@ export class DexSpecies {
479
479
  if (this.dex.currentMod === 'gen7letsgo' && !species.isNonstandard) {
480
480
  const isLetsGo = (
481
481
  (species.num <= 151 || ['Meltan', 'Melmetal'].includes(species.name)) &&
482
- (!species.forme || ['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme))
482
+ (!species.forme || ['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme) &&
483
+ species.name !== 'Pikachu-Alola')
483
484
  );
484
485
  if (!isLetsGo) species.isNonstandard = 'Past';
485
486
  }
487
+ if (this.dex.currentMod === 'gen8bdsp' &&
488
+ (!species.isNonstandard || species.isNonstandard === "Gigantamax")) {
489
+ if (species.gen > 4 || species.num < 1 || species.id === 'pichuspikyeared') {
490
+ species.isNonstandard = 'Past';
491
+ species.tier = species.doublesTier = 'Illegal';
492
+ }
493
+ }
486
494
  species.nfe = !!(species.evos.length && this.get(species.evos[0]).gen <= this.dex.gen);
487
495
  species.canHatch = species.canHatch ||
488
496
  (!['Ditto', 'Undiscovered'].includes(species.eggGroups[0]) && !species.prevo && species.name !== 'Manaphy');
489
497
  if (this.dex.gen === 1) species.bst -= species.baseStats.spd;
490
498
  if (this.dex.gen < 5) delete species.abilities['H'];
499
+ if (this.dex.gen === 3 && this.dex.abilities.get(species.abilities['1']).gen === 4) delete species.abilities['1'];
491
500
  } else {
492
501
  species = new Species({
493
502
  id, name: id,
@@ -301,6 +301,7 @@ export interface ModdedBattleActions {
301
301
  }
302
302
 
303
303
  export interface ModdedBattleSide {
304
+ canDynamaxNow?: (this: Side) => boolean;
304
305
  getRequestData?: (this: Side, forAlly?: boolean) => {name: string, id: ID, pokemon: AnyObject[]};
305
306
  }
306
307
 
@@ -301,6 +301,7 @@ interface ModdedBattleActions {
301
301
  }
302
302
 
303
303
  interface ModdedBattleSide {
304
+ canDynamaxNow?: (this: Side) => boolean;
304
305
  getRequestData?: (this: Side, forAlly?: boolean) => {name: string, id: ID, pokemon: AnyObject[]};
305
306
  }
306
307
 
package/sim/side.ts CHANGED
@@ -255,14 +255,14 @@ export class Side {
255
255
  allies(all?: boolean) {
256
256
  // called during the first switch-in, so `active` can still contain nulls at this point
257
257
  let allies = this.activeTeam().filter(ally => ally);
258
- if (!all) allies = allies.filter(ally => !ally.fainted);
258
+ if (!all) allies = allies.filter(ally => !!ally.hp);
259
259
 
260
260
  return allies;
261
261
  }
262
262
  foes(all?: boolean) {
263
263
  if (this.battle.gameType === 'freeforall') {
264
264
  return this.battle.sides.map(side => side.active[0])
265
- .filter(pokemon => pokemon && pokemon.side !== this && (all || !pokemon.fainted));
265
+ .filter(pokemon => pokemon && pokemon.side !== this && (all || !!pokemon.hp));
266
266
  }
267
267
  return this.foe.allies(all);
268
268
  }
@@ -540,7 +540,7 @@ export class TeamValidator {
540
540
  problem = this.checkItem(set, item, setHas);
541
541
  if (problem) problems.push(problem);
542
542
  if (ruleTable.has('obtainablemisc')) {
543
- if (dex.gen <= 1 || ruleTable.has('allowavs')) {
543
+ if (dex.gen <= 1) {
544
544
  if (item.id) {
545
545
  // no items allowed
546
546
  set.item = '';
@@ -1910,7 +1910,7 @@ export class TeamValidator {
1910
1910
  /**
1911
1911
  * The format allows Sketch to copy moves in Gen 8
1912
1912
  */
1913
- const canSketchGen8Moves = ruleTable.has('sketchgen8moves');
1913
+ const canSketchGen8Moves = ruleTable.has('sketchgen8moves') || this.dex.currentMod === 'gen8bdsp';
1914
1914
 
1915
1915
  let tradebackEligible = false;
1916
1916
  while (species?.name && !alreadyChecked[species.id]) {
@@ -1,3 +1,5 @@
1
+ import {PokemonSet} from '../exported-global-types';
2
+
1
3
  /**
2
4
  * Battle Simulator runner.
3
5
  * Pokemon Showdown - http://pokemonshowdown.com/