@pkmn/sim 0.5.22 → 0.5.25

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 (86) hide show
  1. package/LICENSE +1 -1
  2. package/build/config/formats.js +581 -481
  3. package/build/config/formats.js.map +1 -1
  4. package/build/data/aliases.js +9 -5
  5. package/build/data/aliases.js.map +1 -1
  6. package/build/data/conditions.js +1 -1
  7. package/build/data/conditions.js.map +1 -1
  8. package/build/data/formats-data.js +830 -28
  9. package/build/data/formats-data.js.map +1 -1
  10. package/build/data/learnsets.js +64 -39
  11. package/build/data/learnsets.js.map +1 -1
  12. package/build/data/mods/gen1/formats-data.js +1 -1
  13. package/build/data/mods/gen1/formats-data.js.map +1 -1
  14. package/build/data/mods/gen1/moves.js +5 -0
  15. package/build/data/mods/gen1/moves.js.map +1 -1
  16. package/build/data/mods/gen1/scripts.js +4 -2
  17. package/build/data/mods/gen1/scripts.js.map +1 -1
  18. package/build/data/mods/gen2/moves.js +0 -5
  19. package/build/data/mods/gen2/moves.js.map +1 -1
  20. package/build/data/mods/gen2/rulesets.js +7 -1
  21. package/build/data/mods/gen2/rulesets.js.map +1 -1
  22. package/build/data/mods/gen3/formats-data.js +3 -3
  23. package/build/data/mods/gen3/formats-data.js.map +1 -1
  24. package/build/data/mods/gen3/rulesets.js +1 -1
  25. package/build/data/mods/gen3/rulesets.js.map +1 -1
  26. package/build/data/mods/gen4/rulesets.js +1 -1
  27. package/build/data/mods/gen4/rulesets.js.map +1 -1
  28. package/build/data/mods/gen5/rulesets.js +1 -1
  29. package/build/data/mods/gen5/rulesets.js.map +1 -1
  30. package/build/data/mods/gen6/formats-data.js +64 -64
  31. package/build/data/mods/gen6/formats-data.js.map +1 -1
  32. package/build/data/mods/gen7/rulesets.js +1 -1
  33. package/build/data/mods/gen7/rulesets.js.map +1 -1
  34. package/build/data/pokedex.js +1 -1
  35. package/build/data/rulesets.js +232 -3
  36. package/build/data/rulesets.js.map +1 -1
  37. package/build/data/tags.js +26 -9
  38. package/build/data/tags.js.map +1 -1
  39. package/build/lib/utils.d.ts +4 -0
  40. package/build/lib/utils.js +20 -1
  41. package/build/lib/utils.js.map +1 -1
  42. package/build/sim/battle-stream.js +3 -0
  43. package/build/sim/battle-stream.js.map +1 -1
  44. package/build/sim/battle.d.ts +1 -1
  45. package/build/sim/battle.js.map +1 -1
  46. package/build/sim/dex-conditions.d.ts +1 -0
  47. package/build/sim/dex-conditions.js.map +1 -1
  48. package/build/sim/dex-species.d.ts +5 -0
  49. package/build/sim/dex-species.js +11 -3
  50. package/build/sim/dex-species.js.map +1 -1
  51. package/build/sim/exported-global-types.d.ts +4 -0
  52. package/build/sim/global-types.d.ts +4 -0
  53. package/build/sim/pokemon.d.ts +1 -0
  54. package/build/sim/pokemon.js +1 -0
  55. package/build/sim/pokemon.js.map +1 -1
  56. package/build/sim/team-validator.js +5 -1
  57. package/build/sim/team-validator.js.map +1 -1
  58. package/config/formats.ts +575 -466
  59. package/data/aliases.ts +9 -5
  60. package/data/conditions.ts +1 -1
  61. package/data/formats-data.ts +830 -28
  62. package/data/learnsets.ts +64 -39
  63. package/data/mods/gen1/formats-data.ts +1 -1
  64. package/data/mods/gen1/moves.ts +5 -0
  65. package/data/mods/gen1/scripts.ts +3 -2
  66. package/data/mods/gen2/moves.ts +0 -5
  67. package/data/mods/gen2/rulesets.ts +8 -1
  68. package/data/mods/gen3/formats-data.ts +3 -3
  69. package/data/mods/gen3/rulesets.ts +1 -1
  70. package/data/mods/gen4/rulesets.ts +1 -1
  71. package/data/mods/gen5/rulesets.ts +1 -1
  72. package/data/mods/gen6/formats-data.ts +64 -64
  73. package/data/mods/gen7/rulesets.ts +1 -1
  74. package/data/pokedex.ts +1 -1
  75. package/data/rulesets.ts +212 -3
  76. package/data/tags.ts +27 -9
  77. package/lib/utils.ts +16 -0
  78. package/package.json +2 -2
  79. package/sim/battle-stream.ts +3 -0
  80. package/sim/battle.ts +1 -1
  81. package/sim/dex-conditions.ts +1 -0
  82. package/sim/dex-species.ts +15 -3
  83. package/sim/exported-global-types.ts +4 -0
  84. package/sim/global-types.ts +4 -0
  85. package/sim/pokemon.ts +2 -0
  86. package/sim/team-validator.ts +6 -1
package/data/rulesets.ts CHANGED
@@ -14,7 +14,7 @@ export const Rulesets: {[k: string]: FormatData} = {
14
14
  name: 'Standard',
15
15
  desc: "The standard ruleset for all offical Smogon singles tiers (Ubers, OU, etc.)",
16
16
  ruleset: [
17
- 'Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
17
+ 'Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
18
18
  ],
19
19
  },
20
20
  standardnext: {
@@ -71,6 +71,14 @@ export const Rulesets: {[k: string]: FormatData} = {
71
71
  'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Gravity Sleep Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
72
72
  ],
73
73
  },
74
+ standardoms: {
75
+ effectType: 'ValidatorRule',
76
+ name: 'Standard OMs',
77
+ desc: "The standard ruleset for all Smogon OMs (Almost Any Ability, STABmons, etc.)",
78
+ ruleset: [
79
+ 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'Dynamax Clause', 'HP Percentage Mod', 'Cancel Mod', 'Overflow Stat Mod',
80
+ ],
81
+ },
74
82
  standardnatdex: {
75
83
  effectType: 'ValidatorRule',
76
84
  name: 'Standard NatDex',
@@ -643,6 +651,15 @@ export const Rulesets: {[k: string]: FormatData} = {
643
651
  return problems;
644
652
  },
645
653
  },
654
+ evasionclause: {
655
+ effectType: 'ValidatorRule',
656
+ name: 'Evasion Clause',
657
+ desc: "Bans abilities, items, and moves that boost Evasion",
658
+ ruleset: ['Evasion Abilities Clause', 'Evasion Items Clause', 'Evasion Moves Clause'],
659
+ onBegin() {
660
+ this.add('rule', 'Evasion Clause: Evasion abilities, items, and moves are banned');
661
+ },
662
+ },
646
663
  evasionabilitiesclause: {
647
664
  effectType: 'ValidatorRule',
648
665
  name: 'Evasion Abilities Clause',
@@ -652,6 +669,15 @@ export const Rulesets: {[k: string]: FormatData} = {
652
669
  this.add('rule', 'Evasion Abilities Clause: Evasion abilities are banned');
653
670
  },
654
671
  },
672
+ evasionitemsclause: {
673
+ effectType: 'ValidatorRule',
674
+ name: 'Evasion Items Clause',
675
+ desc: "Bans moves that lower the accuracy of moves used against the user",
676
+ banlist: ['Bright Powder', 'Lax Incense'],
677
+ onBegin() {
678
+ this.add('rule', 'Evasion Items Clause: Evasion items are banned');
679
+ },
680
+ },
655
681
  evasionmovesclause: {
656
682
  effectType: 'ValidatorRule',
657
683
  name: 'Evasion Moves Clause',
@@ -1409,6 +1435,7 @@ export const Rulesets: {[k: string]: FormatData} = {
1409
1435
  name: 'Team Type Preview',
1410
1436
  desc: "Allows each player to see the Pokémon on their opponent's team and those Pokémon's types before they choose their lead Pokémon",
1411
1437
  onTeamPreview() {
1438
+ this.add('clearpoke');
1412
1439
  for (const side of this.sides) {
1413
1440
  for (const pokemon of side.pokemon) {
1414
1441
  const details = pokemon.details.replace(', shiny', '')
@@ -1869,8 +1896,7 @@ export const Rulesets: {[k: string]: FormatData} = {
1869
1896
  reevolutionmod: {
1870
1897
  effectType: "Rule",
1871
1898
  name: "Re-Evolution Mod",
1872
- desc: "Pokémon gain the boosts they would gain from evolving again",
1873
- ruleset: ['Overflow Stat Mod'],
1899
+ desc: "Pokémon gain the stat changes they would gain from evolving again.",
1874
1900
  onBegin() {
1875
1901
  this.add('rule', 'Re-Evolution Mod: Pok\u00e9mon gain the boosts they would gain from evolving again');
1876
1902
  },
@@ -1888,4 +1914,187 @@ export const Rulesets: {[k: string]: FormatData} = {
1888
1914
  return newSpecies;
1889
1915
  },
1890
1916
  },
1917
+ brokenrecordmod: {
1918
+ effectType: "Rule",
1919
+ name: "Broken Record Mod",
1920
+ desc: `Pokémon can hold a TR to use that move in battle.`,
1921
+ onValidateSet(set) {
1922
+ if (!set.item) return;
1923
+ const item = this.dex.items.get(set.item);
1924
+ if (!/^tr\d\d/i.test(item.name)) return;
1925
+ const moveName = item.desc.split('move ')[1].split('.')[0];
1926
+ if (set.moves.map(this.toID).includes(this.toID(moveName))) {
1927
+ return [
1928
+ `${set.species} can't run ${item.name} (${moveName}) as its item because it already has that move in its moveset.`,
1929
+ ];
1930
+ }
1931
+ },
1932
+ onValidateTeam(team) {
1933
+ const trs = new Set<string>();
1934
+ for (const set of team) {
1935
+ if (!set.item) continue;
1936
+ const item = this.dex.items.get(set.item).name;
1937
+ if (!/^tr\d\d/i.test(item)) continue;
1938
+ if (trs.has(item)) {
1939
+ return [`Your team already has a Pok\u00e9mon with ${item}.`];
1940
+ }
1941
+ trs.add(item);
1942
+ }
1943
+ },
1944
+ onTakeItem(item) {
1945
+ return !/^tr\d\d/i.test(item.name);
1946
+ },
1947
+ onModifyMove(move) {
1948
+ if (move.id === 'knockoff') {
1949
+ move.onBasePower = function (basePower, source, target, m) {
1950
+ const item = target.getItem();
1951
+ if (!this.singleEvent('TakeItem', item, target.itemState, target, target, m, item)) return;
1952
+ // Very hardcode but I'd prefer to not make a mod for one damage calculation change
1953
+ if (item.id && !/^tr\d\d/i.test(item.id)) {
1954
+ return this.chainModify(1.5);
1955
+ }
1956
+ };
1957
+ } else if (move.id === 'fling') {
1958
+ move.onPrepareHit = function (target, source, m) {
1959
+ if (source.ignoringItem()) return false;
1960
+ const item = source.getItem();
1961
+ if (!this.singleEvent('TakeItem', item, source.itemState, source, source, m, item)) return false;
1962
+ if (!item.fling) return false;
1963
+ if (/^tr\d\d/i.test(item.id)) return false;
1964
+ m.basePower = item.fling.basePower;
1965
+ if (item.isBerry) {
1966
+ m.onHit = function (foe) {
1967
+ if (this.singleEvent('Eat', item, null, foe, null, null)) {
1968
+ this.runEvent('EatItem', foe, null, null, item);
1969
+ if (item.id === 'leppaberry') foe.staleness = 'external';
1970
+ }
1971
+ if (item.onEat) foe.ateBerry = true;
1972
+ };
1973
+ } else if (item.fling.effect) {
1974
+ m.onHit = item.fling.effect;
1975
+ } else {
1976
+ if (!m.secondaries) m.secondaries = [];
1977
+ if (item.fling.status) {
1978
+ m.secondaries.push({status: item.fling.status});
1979
+ } else if (item.fling.volatileStatus) {
1980
+ m.secondaries.push({volatileStatus: item.fling.volatileStatus});
1981
+ }
1982
+ }
1983
+ source.addVolatile('fling');
1984
+ };
1985
+ }
1986
+ },
1987
+ onBegin() {
1988
+ for (const pokemon of this.getAllPokemon()) {
1989
+ const item = pokemon.getItem();
1990
+ if (/^tr\d\d/i.test(item.name)) {
1991
+ const move = this.dex.moves.get(item.desc.split('move ')[1].split('.')[0]);
1992
+ pokemon.moveSlots = (pokemon as any).baseMoveSlots = [
1993
+ ...pokemon.baseMoveSlots, {
1994
+ id: move.id,
1995
+ move: move.name,
1996
+ pp: move.pp * 8 / 5,
1997
+ maxpp: move.pp * 8 / 5,
1998
+ target: move.target,
1999
+ disabled: false,
2000
+ disabledSource: '',
2001
+ used: false,
2002
+ },
2003
+ ];
2004
+ }
2005
+ }
2006
+ },
2007
+ },
2008
+ categoryswapmod: {
2009
+ effectType: 'Rule',
2010
+ name: 'Category Swap Mod',
2011
+ desc: `All physical moves become special, and all special moves become physical.`,
2012
+ onBegin() {
2013
+ this.add('rule', 'Category Swap Mod: All physical moves become special, and vice versa');
2014
+ },
2015
+ onModifyMove(move, pokemon, target) {
2016
+ if (move.category === "Status") return;
2017
+
2018
+ if (move.category === "Physical") {
2019
+ move.category = "Special";
2020
+ } else if (move.category === "Special") {
2021
+ move.category = "Physical";
2022
+ }
2023
+
2024
+ switch (move.id) {
2025
+ case 'doomdesire': {
2026
+ move.onTry = function (source, subtarget) {
2027
+ if (!subtarget.side.addSlotCondition(subtarget, 'futuremove')) return false;
2028
+ Object.assign(subtarget.side.slotConditions[subtarget.position]['futuremove'], {
2029
+ move: 'doomdesire',
2030
+ source: source,
2031
+ moveData: {
2032
+ id: 'doomdesire',
2033
+ name: "Doom Desire",
2034
+ accuracy: 100,
2035
+ basePower: 140,
2036
+ category: "Physical",
2037
+ priority: 0,
2038
+ flags: {},
2039
+ effectType: 'Move',
2040
+ isFutureMove: true,
2041
+ type: 'Steel',
2042
+ },
2043
+ });
2044
+ this.add('-start', source, 'Doom Desire');
2045
+ return this.NOT_FAIL;
2046
+ };
2047
+ break;
2048
+ }
2049
+ case 'futuresight': {
2050
+ move.onTry = function (source, subtarget) {
2051
+ if (!subtarget.side.addSlotCondition(subtarget, 'futuremove')) return false;
2052
+ Object.assign(subtarget.side.slotConditions[subtarget.position]['futuremove'], {
2053
+ duration: 3,
2054
+ move: 'futuresight',
2055
+ source: source,
2056
+ moveData: {
2057
+ id: 'futuresight',
2058
+ name: "Future Sight",
2059
+ accuracy: 100,
2060
+ basePower: 120,
2061
+ category: "Physical",
2062
+ priority: 0,
2063
+ flags: {},
2064
+ ignoreImmunity: false,
2065
+ effectType: 'Move',
2066
+ isFutureMove: true,
2067
+ type: 'Psychic',
2068
+ },
2069
+ });
2070
+ this.add('-start', source, 'move: Future Sight');
2071
+ return this.NOT_FAIL;
2072
+ };
2073
+ break;
2074
+ }
2075
+ // Moves with dynamic categories will always be physical if not special-cased
2076
+ case 'lightthatburnsthesky':
2077
+ case 'photongeyser': {
2078
+ move.category = 'Special';
2079
+ if (pokemon.getStat('atk', false, true) > pokemon.getStat('spa', false, true)) move.category = 'Physical';
2080
+ break;
2081
+ }
2082
+ case 'shellsidearm': {
2083
+ if (!target) return;
2084
+ move.category = 'Special';
2085
+ const atk = pokemon.getStat('atk', false, true);
2086
+ const spa = pokemon.getStat('spa', false, true);
2087
+ const def = target.getStat('def', false, true);
2088
+ const spd = target.getStat('spd', false, true);
2089
+ const physical = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * atk) / def) / 50);
2090
+ const special = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * spa) / spd) / 50);
2091
+ if (physical > special || (physical === special && this.random(2) === 0)) {
2092
+ move.category = 'Physical';
2093
+ move.flags.contact = 1;
2094
+ }
2095
+ break;
2096
+ }
2097
+ }
2098
+ },
2099
+ },
1891
2100
  };
package/data/tags.ts CHANGED
@@ -194,15 +194,6 @@ export const Tags: {[id: string]: TagData} = {
194
194
  name: "AG",
195
195
  speciesFilter: species => species.tier === 'AG',
196
196
  },
197
- nduubl: {
198
- name: "ND UUBL",
199
- speciesFilter: species => [
200
- 'Aerodactyl-Mega', 'Alakazam', 'Blacephalon', 'Blaziken', 'Diancie-Mega', 'Gallade-Mega', 'Gardevoir-Mega', 'Gengar', 'Gyarados',
201
- 'Gyarados-Mega', 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios',
202
- 'Latios-Mega', 'Manaphy', 'Medicham-Mega', 'Melmetal', 'Mew', 'Moltres-Galar', 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega',
203
- 'Slowking-Galar', 'Thundurus', 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
204
- ].includes(species.name),
205
- },
206
197
 
207
198
  // Doubles tiers
208
199
  // -------------
@@ -227,6 +218,33 @@ export const Tags: {[id: string]: TagData} = {
227
218
  speciesFilter: species => species.doublesTier === '(DUU)',
228
219
  },
229
220
 
221
+ // Nat Dex tiers
222
+ // -------------
223
+ nduber: {
224
+ name: "ND Uber",
225
+ speciesFilter: species => species.natDexTier === 'Uber' || species.natDexTier === '(Uber)',
226
+ },
227
+ ndou: {
228
+ name: "ND OU",
229
+ speciesFilter: species => species.natDexTier === 'OU' || species.natDexTier === '(OU)',
230
+ },
231
+ nduubl: {
232
+ name: "ND UUBL",
233
+ speciesFilter: species => species.natDexTier === 'UUBL',
234
+ },
235
+ nduu: {
236
+ name: "ND UU",
237
+ speciesFilter: species => species.natDexTier === 'UU',
238
+ },
239
+ ndrubl: {
240
+ name: "ND RUBL",
241
+ speciesFilter: species => species.natDexTier === 'RUBL',
242
+ },
243
+ ndru: {
244
+ name: "ND RU",
245
+ speciesFilter: species => species.natDexTier === 'RU',
246
+ },
247
+
230
248
  // Legality tags
231
249
  past: {
232
250
  name: "Past",
package/lib/utils.ts CHANGED
@@ -63,6 +63,22 @@ export function stripHTML(htmlContent: string) {
63
63
  return htmlContent.replace(/<[^>]*>/g, '');
64
64
  }
65
65
 
66
+ /**
67
+ * Maps numbers to their ordinal string.
68
+ */
69
+ export function formatOrder(place: number) {
70
+ // anything between 10 and 20 should always end with -th
71
+ let remainder = place % 100;
72
+ if (remainder >= 10 && remainder <= 20) return place + 'th';
73
+
74
+ // follow standard rules with -st, -nd, -rd, and -th
75
+ remainder = place % 10;
76
+ if (remainder === 1) return place + 'st';
77
+ if (remainder === 2) return place + 'nd';
78
+ if (remainder === 3) return place + 'rd';
79
+ return place + 'th';
80
+ }
81
+
66
82
  /**
67
83
  * Visualizes eval output in a slightly more readable form
68
84
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.5.22",
3
+ "version": "0.5.25",
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,7 @@
27
27
  "sim"
28
28
  ],
29
29
  "dependencies": {
30
- "@pkmn/sets": "^3.0.0",
30
+ "@pkmn/sets": "^3.1.1",
31
31
  "@pkmn/streams": "^1.0.0"
32
32
  },
33
33
  "devDependencies": {
@@ -211,6 +211,9 @@ export class BattleStream extends Streams.ObjectReadWriteStream<string> {
211
211
  case 'requestlog':
212
212
  this.push(`requesteddata\n${this.battle!.inputLog.join('\n')}`);
213
213
  break;
214
+ case 'requestexport':
215
+ this.push(`requesteddata\n${this.battle!.prngSeed}\n${this.battle!.inputLog.join('\n')}`);
216
+ break;
214
217
  case 'requestteam':
215
218
  message = message.trim();
216
219
  const slotNum = parseInt(message.slice(1)) - 1;
package/sim/battle.ts CHANGED
@@ -399,7 +399,7 @@ export class Battle {
399
399
  }
400
400
 
401
401
  /** Sort a list, resolving speed ties the way the games do. */
402
- speedSort<T>(list: T[], comparator: (a: T, b: T) => number = this.comparePriority) {
402
+ speedSort<T extends AnyObject>(list: T[], comparator: (a: T, b: T) => number = this.comparePriority) {
403
403
  if (list.length < 2) return;
404
404
  let sorted = 0;
405
405
  // This is a Selection Sort - not the fastest sort in general, but
@@ -473,6 +473,7 @@ export interface EventMethods {
473
473
  onModifyPriorityPriority?: number;
474
474
  onModifySpAPriority?: number;
475
475
  onModifySpDPriority?: number;
476
+ onModifySpePriority?: number;
476
477
  onModifyTypePriority?: number;
477
478
  onModifyWeightPriority?: number;
478
479
  onRedirectTargetPriority?: number;
@@ -42,6 +42,7 @@ export interface SpeciesFormatsData {
42
42
  exclusiveMoves?: readonly string[];
43
43
  gmaxUnreleased?: boolean;
44
44
  isNonstandard?: Nonstandard | null;
45
+ natDexTier?: TierTypes.Singles | TierTypes.Other;
45
46
  randomBattleMoves?: readonly string[];
46
47
  randomBattleLevel?: number;
47
48
  randomDoubleBattleMoves?: readonly string[];
@@ -231,6 +232,10 @@ export class Species extends BasicEffect implements Readonly<BasicEffect & Speci
231
232
  * Doubles Tier. The Pokemon's location in the Smogon doubles tier system.
232
233
  */
233
234
  readonly doublesTier: TierTypes.Doubles | TierTypes.Other;
235
+ /**
236
+ * National Dex Tier. The Pokemon's location in the Smogon National Dex tier system.
237
+ */
238
+ readonly natDexTier: TierTypes.Singles | TierTypes.Other;
234
239
  declare readonly randomBattleMoves?: readonly ID[];
235
240
  declare readonly randomBattleLevel?: number;
236
241
  declare readonly randomDoubleBattleMoves?: readonly ID[];
@@ -261,6 +266,7 @@ export class Species extends BasicEffect implements Readonly<BasicEffect & Speci
261
266
  this.prevo = data.prevo || '';
262
267
  this.tier = data.tier || '';
263
268
  this.doublesTier = data.doublesTier || '';
269
+ this.natDexTier = data.natDexTier || '';
264
270
  this.evos = data.evos || [];
265
271
  this.evoType = data.evoType || undefined;
266
272
  this.evoMove = data.evoMove || undefined;
@@ -451,16 +457,19 @@ export class DexSpecies {
451
457
  if (!(key in species)) (species as any)[key] = baseSpeciesStatuses[key];
452
458
  }
453
459
  }
454
- if (!species.tier && !species.doublesTier && species.baseSpecies !== species.name) {
460
+ if (!species.tier && !species.doublesTier && !species.natDexTier && species.baseSpecies !== species.name) {
455
461
  if (species.baseSpecies === 'Mimikyu') {
456
462
  species.tier = this.dex.data.FormatsData[toID(species.baseSpecies)].tier || 'Illegal';
457
463
  species.doublesTier = this.dex.data.FormatsData[toID(species.baseSpecies)].doublesTier || 'Illegal';
464
+ species.natDexTier = this.dex.data.FormatsData[toID(species.baseSpecies)].natDexTier || 'Illegal';
458
465
  } else if (species.id.endsWith('totem')) {
459
466
  species.tier = this.dex.data.FormatsData[species.id.slice(0, -5)].tier || 'Illegal';
460
467
  species.doublesTier = this.dex.data.FormatsData[species.id.slice(0, -5)].doublesTier || 'Illegal';
468
+ species.natDexTier = this.dex.data.FormatsData[species.id.slice(0, -5)].natDexTier || 'Illegal';
461
469
  } else if (species.battleOnly) {
462
470
  species.tier = this.dex.data.FormatsData[toID(species.battleOnly)].tier || 'Illegal';
463
471
  species.doublesTier = this.dex.data.FormatsData[toID(species.battleOnly)].doublesTier || 'Illegal';
472
+ species.natDexTier = this.dex.data.FormatsData[toID(species.battleOnly)].natDexTier || 'Illegal';
464
473
  } else {
465
474
  const baseFormatsData = this.dex.data.FormatsData[toID(species.baseSpecies)];
466
475
  if (!baseFormatsData) {
@@ -468,13 +477,16 @@ export class DexSpecies {
468
477
  }
469
478
  species.tier = baseFormatsData.tier || 'Illegal';
470
479
  species.doublesTier = baseFormatsData.doublesTier || 'Illegal';
480
+ species.natDexTier = baseFormatsData.natDexTier || 'Illegal';
471
481
  }
472
482
  }
473
483
  if (!species.tier) species.tier = 'Illegal';
474
484
  if (!species.doublesTier) species.doublesTier = species.tier as any;
485
+ if (!species.natDexTier) species.natDexTier = species.tier;
475
486
  if (species.gen > this.dex.gen) {
476
487
  species.tier = 'Illegal';
477
488
  species.doublesTier = 'Illegal';
489
+ species.natDexTier = 'Illegal';
478
490
  species.isNonstandard = 'Future';
479
491
  }
480
492
  if (this.dex.currentMod === 'gen7letsgo' && !species.isNonstandard) {
@@ -490,7 +502,7 @@ export class DexSpecies {
490
502
  if (species.gen > 4 || (species.num < 1 && species.isNonstandard !== 'CAP') ||
491
503
  species.id === 'pichuspikyeared') {
492
504
  species.isNonstandard = 'Future';
493
- species.tier = species.doublesTier = 'Illegal';
505
+ species.tier = species.doublesTier = species.natDexTier = 'Illegal';
494
506
  }
495
507
  }
496
508
  species.nfe = species.evos.some(evo => {
@@ -505,7 +517,7 @@ export class DexSpecies {
505
517
  } else {
506
518
  species = new Species({
507
519
  id, name: id,
508
- exists: false, tier: 'Illegal', doublesTier: 'Illegal', isNonstandard: 'Custom',
520
+ exists: false, tier: 'Illegal', doublesTier: 'Illegal', natDexTier: 'Illegal', isNonstandard: 'Custom',
509
521
  });
510
522
  }
511
523
  (species as any).kind = 'Species';
@@ -312,6 +312,7 @@ export interface ModdedBattlePokemon {
312
312
  clearBoosts?: (this: Pokemon) => void;
313
313
  calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
314
314
  cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
315
+ eatItem?: (this: Pokemon, force?: boolean, source?: Pokemon, sourceEffect?: Effect) => boolean;
315
316
  formeChange?: (
316
317
  this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
317
318
  ) => boolean;
@@ -344,6 +345,7 @@ export interface ModdedBattlePokemon {
344
345
  ) => boolean;
345
346
  takeItem?: (this: Pokemon, source: Pokemon | undefined) => boolean | Item;
346
347
  transformInto?: (this: Pokemon, pokemon: Pokemon, effect: Effect | null) => boolean;
348
+ useItem?: (this: Pokemon, source?: Pokemon, sourceEffect?: Effect) => boolean;
347
349
  ignoringAbility?: (this: Pokemon) => boolean;
348
350
  ignoringItem?: (this: Pokemon) => boolean;
349
351
 
@@ -515,6 +517,7 @@ export namespace RandomTeamsTypes {
515
517
  shiny: boolean;
516
518
  nature?: string;
517
519
  happiness?: number;
520
+ dynamaxLevel?: number;
518
521
  gigantamax?: boolean;
519
522
  }
520
523
  export interface RandomFactorySet {
@@ -530,6 +533,7 @@ export namespace RandomTeamsTypes {
530
533
  ivs: SparseStatsTable;
531
534
  nature: string;
532
535
  moves: string[];
536
+ dynamaxLevel?: number;
533
537
  gigantamax?: boolean;
534
538
  }
535
539
  }
@@ -312,6 +312,7 @@ interface ModdedBattlePokemon {
312
312
  clearBoosts?: (this: Pokemon) => void;
313
313
  calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
314
314
  cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
315
+ eatItem?: (this: Pokemon, force?: boolean, source?: Pokemon, sourceEffect?: Effect) => boolean;
315
316
  formeChange?: (
316
317
  this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
317
318
  ) => boolean;
@@ -344,6 +345,7 @@ interface ModdedBattlePokemon {
344
345
  ) => boolean;
345
346
  takeItem?: (this: Pokemon, source: Pokemon | undefined) => boolean | Item;
346
347
  transformInto?: (this: Pokemon, pokemon: Pokemon, effect: Effect | null) => boolean;
348
+ useItem?: (this: Pokemon, source?: Pokemon, sourceEffect?: Effect) => boolean;
347
349
  ignoringAbility?: (this: Pokemon) => boolean;
348
350
  ignoringItem?: (this: Pokemon) => boolean;
349
351
 
@@ -515,6 +517,7 @@ namespace RandomTeamsTypes {
515
517
  shiny: boolean;
516
518
  nature?: string;
517
519
  happiness?: number;
520
+ dynamaxLevel?: number;
518
521
  gigantamax?: boolean;
519
522
  }
520
523
  export interface RandomFactorySet {
@@ -530,6 +533,7 @@ namespace RandomTeamsTypes {
530
533
  ivs: SparseStatsTable;
531
534
  nature: string;
532
535
  moves: string[];
536
+ dynamaxLevel?: number;
533
537
  gigantamax?: boolean;
534
538
  }
535
539
  }
package/sim/pokemon.ts CHANGED
@@ -77,6 +77,7 @@ export class Pokemon {
77
77
  readonly gender: GenderName;
78
78
  readonly happiness: number;
79
79
  readonly pokeball: string;
80
+ readonly dynamaxLevel: number;
80
81
  readonly gigantamax: boolean;
81
82
 
82
83
  /** Transform keeps the original pre-transformed Hidden Power in Gen 2-4. */
@@ -326,6 +327,7 @@ export class Pokemon {
326
327
  if (this.gender === 'N') this.gender = '';
327
328
  this.happiness = typeof set.happiness === 'number' ? this.battle.clampIntRange(set.happiness, 0, 255) : 255;
328
329
  this.pokeball = this.set.pokeball || 'pokeball';
330
+ this.dynamaxLevel = typeof set.dynamaxLevel === 'number' ? this.battle.clampIntRange(set.dynamaxLevel, 0, 10) : 10;
329
331
  this.gigantamax = this.set.gigantamax || false;
330
332
 
331
333
  this.baseMoveSlots = [];
@@ -1366,6 +1366,11 @@ export class TeamValidator {
1366
1366
  const doublesTierTag = 'pokemontag:' + toID(doublesTier);
1367
1367
  setHas[doublesTierTag] = true;
1368
1368
 
1369
+ const ndTier = tierSpecies.natDexTier === '(PU)' ? 'ZU' :
1370
+ tierSpecies.natDexTier === '(NU)' ? 'PU' : tierSpecies.natDexTier;
1371
+ const ndTierTag = 'pokemontag:nd' + toID(ndTier);
1372
+ setHas[ndTierTag] = true;
1373
+
1369
1374
  // Only pokemon that can gigantamax should have the Gmax flag
1370
1375
  if (!tierSpecies.canGigantamax && set.gigantamax) {
1371
1376
  return `${tierSpecies.name} cannot Gigantamax but is flagged as being able to.`;
@@ -2206,7 +2211,7 @@ export class TeamValidator {
2206
2211
  // different learnsets. To prevent a leak, we make them show up as their
2207
2212
  // base forme, but hardcode their learnsets into Rockruff-Dusk and
2208
2213
  // Greninja-Ash
2209
- if ((species.baseSpecies === 'Gastrodon' || species.baseSpecies === 'Pumpkaboo') && species.forme) {
2214
+ if (['Gastrodon', 'Pumpkaboo', 'Sinistea'].includes(species.baseSpecies) && species.forme) {
2210
2215
  return this.dex.species.get(species.baseSpecies);
2211
2216
  } else if (species.name === 'Lycanroc-Dusk') {
2212
2217
  return this.dex.species.get('Rockruff-Dusk');