@pkmn/randoms 0.4.24 → 0.5.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.
package/src/gen7.ts CHANGED
@@ -9,7 +9,9 @@ import {
9
9
  PRNGSeed,
10
10
  PlayerOptions,
11
11
  RandomTeamsTypes,
12
+ SparseStatsTable,
12
13
  Species,
14
+ StatID,
13
15
  StatsTable,
14
16
  toID,
15
17
  } from '@pkmn/sim';
@@ -27,6 +29,19 @@ interface BattleFactorySet {
27
29
  evs?: Partial<StatsTable>;
28
30
  ivs?: Partial<StatsTable>;
29
31
  }
32
+
33
+ const ZeroAttackHPIVs: {[k: string]: SparseStatsTable} = {
34
+ grass: {hp: 30, spa: 30},
35
+ fire: {spa: 30, spe: 30},
36
+ ice: {def: 30},
37
+ ground: {spa: 30, spd: 30},
38
+ fighting: {def: 30, spa: 30, spd: 30, spe: 30},
39
+ electric: {def: 30, spe: 30},
40
+ psychic: {spe: 30},
41
+ flying: {spa: 30, spd: 30, spe: 30},
42
+ rock: {def: 30, spd: 30, spe: 30},
43
+ };
44
+
30
45
  export class RandomGen7Teams extends RandomTeams {
31
46
  constructor(dex: ModdedDex, format: Format, prng: PRNG | PRNGSeed | null) {
32
47
  super(dex, format, prng);
@@ -1423,6 +1438,20 @@ export class RandomGen7Teams extends RandomTeams {
1423
1438
  ivs.spe = 0;
1424
1439
  }
1425
1440
 
1441
+ // Fix IVs for non-Bottle Cap-able sets
1442
+ if (hasHiddenPower && level < 100) {
1443
+ let hpType;
1444
+ for (const move of moves) {
1445
+ if (move.startsWith('hiddenpower')) hpType = move.substr(11);
1446
+ }
1447
+ if (!hpType) throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
1448
+ const HPivs = ivs.atk === 0 ? ZeroAttackHPIVs[hpType] : this.dex.types.get(hpType).HPivs;
1449
+ let iv: StatID;
1450
+ for (iv in HPivs) {
1451
+ ivs[iv] = HPivs[iv]!;
1452
+ }
1453
+ }
1454
+
1426
1455
  return {
1427
1456
  name: species.baseSpecies,
1428
1457
  species: forme,
package/src/gen8.ts CHANGED
@@ -934,7 +934,8 @@ export class RandomTeams {
934
934
  !!counter.setupType ||
935
935
  !!counter.get('speedsetup') ||
936
936
  !!teamDetails.stealthRock ||
937
- ['rest', 'substitute', 'trickroom', 'teleport'].some(m => moves.has(m)),
937
+ ['rest', 'substitute', 'trickroom', 'teleport'].some(m => moves.has(m)) ||
938
+ (species.id === 'palossand' && movePool.includes('shoreup')),
938
939
  };
939
940
  case 'stickyweb':
940
941
  return {cull: counter.setupType === 'Special' || !!teamDetails.stickyWeb};
@@ -1231,7 +1232,7 @@ export class RandomTeams {
1231
1232
  isNoDynamax: boolean
1232
1233
  ): boolean {
1233
1234
  if ([
1234
- 'Flare Boost', 'Hydration', 'Ice Body', 'Innards Out', 'Insomnia', 'Misty Surge',
1235
+ 'Flare Boost', 'Hydration', 'Ice Body', 'Immunity', 'Innards Out', 'Insomnia', 'Misty Surge',
1235
1236
  'Quick Feet', 'Rain Dish', 'Snow Cloak', 'Steadfast', 'Steam Engine',
1236
1237
  ].includes(ability)) return true;
1237
1238
 
@@ -1650,7 +1651,6 @@ export class RandomTeams {
1650
1651
  !counter.get('drain') && !counter.get('recoil') && !counter.get('recovery') &&
1651
1652
  ((defensiveStatTotal <= 250 && counter.get('hazards')) || defensiveStatTotal <= 210)
1652
1653
  ) return 'Focus Sash';
1653
- if (!isDoubles && ability === 'Water Bubble') return 'Mystic Water';
1654
1654
  if (
1655
1655
  moves.has('clangoroussoul') ||
1656
1656
  // We manually check for speed-boosting moves, rather than using `counter.get('speedsetup')`,
@@ -1758,12 +1758,19 @@ export class RandomTeams {
1758
1758
 
1759
1759
  const moves = new Set<string>();
1760
1760
  let counter: MoveCounter;
1761
+ // This is just for BDSP Unown;
1762
+ // it can be removed from this file if BDSP gets its own random-teams file in the future.
1763
+ let hasHiddenPower = false;
1761
1764
 
1762
1765
  do {
1763
1766
  // Choose next 4 moves from learnset/viable moves and add them to moves list:
1764
1767
  const pool = (movePool.length ? movePool : rejectedPool);
1765
1768
  while (moves.size < 4 && pool.length) {
1766
1769
  const moveid = this.sampleNoReplace(pool);
1770
+ if (moveid.startsWith('hiddenpower')) {
1771
+ if (hasHiddenPower) continue;
1772
+ hasHiddenPower = true;
1773
+ }
1767
1774
  moves.add(moveid);
1768
1775
  }
1769
1776
 
@@ -1854,17 +1861,33 @@ export class RandomTeams {
1854
1861
 
1855
1862
  // Remove rejected moves from the move list
1856
1863
  if (cull && movePool.length) {
1864
+ if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
1857
1865
  if (move.category !== 'Status' && !move.damage) rejectedPool.push(moveid);
1858
1866
  moves.delete(moveid);
1859
1867
  break;
1860
1868
  }
1861
1869
  if (cull && rejectedPool.length) {
1870
+ if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
1862
1871
  moves.delete(moveid);
1863
1872
  break;
1864
1873
  }
1865
1874
  }
1866
1875
  } while (moves.size < 4 && (movePool.length || rejectedPool.length));
1867
1876
 
1877
+ // for BD/SP only
1878
+ if (hasHiddenPower) {
1879
+ let hpType;
1880
+ for (const move of moves) {
1881
+ if (move.startsWith('hiddenpower')) hpType = move.substr(11);
1882
+ }
1883
+ if (!hpType) throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
1884
+ const HPivs = this.dex.types.get(hpType).HPivs;
1885
+ let iv: StatID;
1886
+ for (iv in HPivs) {
1887
+ ivs[iv] = HPivs[iv]!;
1888
+ }
1889
+ }
1890
+
1868
1891
  const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a));
1869
1892
  Utils.sortBy(abilityData, abil => -abil.rating);
1870
1893
 
@@ -1973,7 +1996,7 @@ export class RandomTeams {
1973
1996
  // No Dmax levelling
1974
1997
  } else if (isNoDynamax) {
1975
1998
  const tier = species.name.endsWith('-Gmax') ? this.dex.species.get(species.changesFrom).tier : species.tier;
1976
- const tierScale: {[k: string]: number} = {
1999
+ const tierScale: Partial<Record<Species['tier'], number>> = {
1977
2000
  Uber: 76,
1978
2001
  OU: 80,
1979
2002
  UUBL: 81,
@@ -1995,11 +2018,11 @@ export class RandomTeams {
1995
2018
  delibird: 100, vespiquen: 96, pikachu: 92, shedinja: 92, solrock: 90, arctozolt: 88, reuniclus: 87,
1996
2019
  decidueye: 87, noivern: 85, magnezone: 82, slowking: 81,
1997
2020
  };
1998
- level = customScale[species.id] || tierScale[tier];
2021
+ level = customScale[species.id] || tierScale[tier] || 80;
1999
2022
  // BDSP tier levelling
2000
2023
  } else if (this.dex.currentMod === 'gen8bdsp') {
2001
- const tierScale: {[k: string]: number} = {
2002
- Uber: 76,
2024
+ const tierScale: Partial<Record<Species['tier'], number>> = {
2025
+ Uber: 76, Unreleased: 76,
2003
2026
  OU: 80,
2004
2027
  UUBL: 81,
2005
2028
  UU: 82,
@@ -2010,10 +2033,9 @@ export class RandomTeams {
2010
2033
  PUBL: 87,
2011
2034
  PU: 88, "(PU)": 88, NFE: 88,
2012
2035
  };
2013
- // to override tier scaling if needed
2014
2036
  const customScale: {[k: string]: number} = {};
2015
2037
 
2016
- level = customScale[species.id] || tierScale[species.tier];
2038
+ level = customScale[species.id] || tierScale[species.tier] || 80;
2017
2039
  // Arbitrary levelling base on data files (typically winrate-influenced)
2018
2040
  } else if (species.randomBattleLevel) {
2019
2041
  level = species.randomBattleLevel;
@@ -2185,7 +2207,10 @@ export class RandomTeams {
2185
2207
  const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
2186
2208
 
2187
2209
  // Limit one Pokemon per tier, two for Monotype
2210
+ // This limitation is not applied to BD/SP team generation, because tiering for BD/SP is not yet complete,
2211
+ // meaning that most Pokémon are in OU.
2188
2212
  if (
2213
+ this.dex.currentMod !== 'gen8bdsp' &&
2189
2214
  (tierCount[tier] >= (this.forceMonotype || isMonotype ? 2 : 1) * limitFactor) &&
2190
2215
  !this.randomChance(1, Math.pow(5, tierCount[tier]))
2191
2216
  ) {
@@ -2215,7 +2240,6 @@ export class RandomTeams {
2215
2240
 
2216
2241
  // Okay, the set passes, add it to our team
2217
2242
  pokemon.push(set);
2218
-
2219
2243
  if (pokemon.length === this.maxTeamSize) {
2220
2244
  // Set Zoroark's level to be the same as the last Pokemon
2221
2245
  const illusion = teamDetails.illusion;