@pkmn/randoms 0.5.1 → 0.5.5
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/build/gen1.js +7 -1
- package/build/gen1.js.map +1 -1
- package/build/gen3.js +10 -2
- package/build/gen3.js.map +1 -1
- package/build/gen5.js +4 -0
- package/build/gen5.js.map +1 -1
- package/build/gen6.js +1 -0
- package/build/gen6.js.map +1 -1
- package/build/gen7.js +3 -0
- package/build/gen7.js.map +1 -1
- package/build/gen8.d.ts +19 -2
- package/build/gen8.js +298 -50
- package/build/gen8.js.map +1 -1
- package/package.json +2 -2
- package/src/gen1.ts +10 -1
- package/src/gen3.ts +9 -2
- package/src/gen5.ts +5 -0
- package/src/gen6.ts +2 -0
- package/src/gen7.ts +6 -0
- package/src/gen8.ts +282 -48
package/src/gen3.ts
CHANGED
|
@@ -28,7 +28,10 @@ export class RandomGen3Teams extends RandomGen4Teams {
|
|
|
28
28
|
Fighting: (movePool, moves, abilities, types, counter) => !counter.get('Fighting'),
|
|
29
29
|
Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
|
|
30
30
|
Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
|
|
31
|
-
Normal: (movePool, moves, abilities, types, counter) =>
|
|
31
|
+
Normal: (movePool, moves, abilities, types, counter, species) => {
|
|
32
|
+
if (species.id === 'blissey' && movePool.includes('softboiled')) return true;
|
|
33
|
+
return !counter.get('Normal') && counter.setupType === 'Physical';
|
|
34
|
+
},
|
|
32
35
|
Psychic: (movePool, moves, abilities, types, counter, species) => (
|
|
33
36
|
types.has('Psychic') &&
|
|
34
37
|
(movePool.includes('psychic') || movePool.includes('psychoboost')) &&
|
|
@@ -41,6 +44,7 @@ export class RandomGen3Teams extends RandomGen4Teams {
|
|
|
41
44
|
// If the Pokémon has this move, the other move will be forced
|
|
42
45
|
protect: movePool => movePool.includes('wish'),
|
|
43
46
|
sunnyday: movePool => movePool.includes('solarbeam'),
|
|
47
|
+
sleeptalk: movePool => movePool.includes('rest'),
|
|
44
48
|
};
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -82,11 +86,12 @@ export class RandomGen3Teams extends RandomGen4Teams {
|
|
|
82
86
|
|
|
83
87
|
// Not very useful without their supporting moves
|
|
84
88
|
case 'amnesia': case 'sleeptalk':
|
|
89
|
+
if (!moves.has('rest')) return {cull: true};
|
|
85
90
|
if (movePool.length > 1) {
|
|
86
91
|
const rest = movePool.indexOf('rest');
|
|
87
92
|
if (rest >= 0) this.fastPop(movePool, rest);
|
|
88
93
|
}
|
|
89
|
-
|
|
94
|
+
break;
|
|
90
95
|
case 'barrier':
|
|
91
96
|
return {cull: !moves.has('calmmind') && !moves.has('batonpass') && !moves.has('mirrorcoat')};
|
|
92
97
|
case 'batonpass':
|
|
@@ -575,6 +580,8 @@ export class RandomGen3Teams extends RandomGen4Teams {
|
|
|
575
580
|
}
|
|
576
581
|
|
|
577
582
|
randomTeam() {
|
|
583
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
584
|
+
|
|
578
585
|
const seed = this.prng.seed;
|
|
579
586
|
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
580
587
|
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
package/src/gen5.ts
CHANGED
|
@@ -729,6 +729,9 @@ export class RandomGen5Teams extends RandomGen6Teams {
|
|
|
729
729
|
NUBL: 86,
|
|
730
730
|
NU: 86,
|
|
731
731
|
'(NU)': 88,
|
|
732
|
+
PUBL: 88,
|
|
733
|
+
PU: 88,
|
|
734
|
+
'(PU)': 90,
|
|
732
735
|
};
|
|
733
736
|
const customScale: {[forme: string]: number} = {
|
|
734
737
|
Delibird: 100, 'Farfetch\u2019d': 100, Luvdisc: 100, Unown: 100,
|
|
@@ -780,6 +783,8 @@ export class RandomGen5Teams extends RandomGen6Teams {
|
|
|
780
783
|
}
|
|
781
784
|
|
|
782
785
|
randomTeam() {
|
|
786
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
787
|
+
|
|
783
788
|
const seed = this.prng.seed;
|
|
784
789
|
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
785
790
|
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
package/src/gen6.ts
CHANGED
|
@@ -1207,6 +1207,8 @@ export class RandomGen6Teams extends RandomGen7Teams {
|
|
|
1207
1207
|
}
|
|
1208
1208
|
|
|
1209
1209
|
randomFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] {
|
|
1210
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
1211
|
+
|
|
1210
1212
|
const forceResult = (depth >= 4);
|
|
1211
1213
|
|
|
1212
1214
|
// The teams generated depend on the tier choice in such a way that
|
package/src/gen7.ts
CHANGED
|
@@ -1467,6 +1467,8 @@ export class RandomGen7Teams extends RandomTeams {
|
|
|
1467
1467
|
}
|
|
1468
1468
|
|
|
1469
1469
|
randomTeam() {
|
|
1470
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
1471
|
+
|
|
1470
1472
|
const seed = this.prng.seed;
|
|
1471
1473
|
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
1472
1474
|
const pokemon = [];
|
|
@@ -1739,6 +1741,8 @@ export class RandomGen7Teams extends RandomTeams {
|
|
|
1739
1741
|
}
|
|
1740
1742
|
|
|
1741
1743
|
randomFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] {
|
|
1744
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
1745
|
+
|
|
1742
1746
|
const forceResult = (depth >= 4);
|
|
1743
1747
|
const isMonotype = !!this.forceMonotype || this.dex.formats.getRuleTable(this.format).has('sametypeclause');
|
|
1744
1748
|
|
|
@@ -2026,6 +2030,8 @@ export class RandomGen7Teams extends RandomTeams {
|
|
|
2026
2030
|
}
|
|
2027
2031
|
|
|
2028
2032
|
randomBSSFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] {
|
|
2033
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2034
|
+
|
|
2029
2035
|
const forceResult = (depth >= 4);
|
|
2030
2036
|
|
|
2031
2037
|
const pokemon = [];
|
package/src/gen8.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {Utils} from './utils';
|
|
2
2
|
import {
|
|
3
|
+
Ability,
|
|
3
4
|
AnyObject,
|
|
5
|
+
BasicEffect,
|
|
6
|
+
Dex,
|
|
4
7
|
Format,
|
|
5
|
-
|
|
8
|
+
Item,
|
|
6
9
|
ModdedDex,
|
|
7
10
|
Move,
|
|
11
|
+
Nature,
|
|
8
12
|
PRNG,
|
|
9
13
|
PRNGSeed,
|
|
10
14
|
PlayerOptions,
|
|
@@ -13,6 +17,7 @@ import {
|
|
|
13
17
|
Species,
|
|
14
18
|
StatID,
|
|
15
19
|
StatsTable,
|
|
20
|
+
Tags,
|
|
16
21
|
toID,
|
|
17
22
|
} from '@pkmn/sim';
|
|
18
23
|
|
|
@@ -86,6 +91,10 @@ const Hazards = [
|
|
|
86
91
|
'spikes', 'stealthrock', 'stickyweb', 'toxicspikes',
|
|
87
92
|
];
|
|
88
93
|
|
|
94
|
+
function sereneGraceBenefits(move: Move) {
|
|
95
|
+
return move.secondary?.chance && move.secondary.chance >= 20 && move.secondary.chance < 100;
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
export class RandomTeams {
|
|
90
99
|
dex: ModdedDex;
|
|
91
100
|
gen: number;
|
|
@@ -272,6 +281,7 @@ export class RandomTeams {
|
|
|
272
281
|
*/
|
|
273
282
|
sampleNoReplace(list: any[]) {
|
|
274
283
|
const length = list.length;
|
|
284
|
+
if (length === 0) return null;
|
|
275
285
|
const index = this.random(length);
|
|
276
286
|
return this.fastPop(list, index);
|
|
277
287
|
}
|
|
@@ -290,6 +300,54 @@ export class RandomTeams {
|
|
|
290
300
|
return samples;
|
|
291
301
|
}
|
|
292
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Check if user has directly tried to ban/unban/restrict things in a custom battle.
|
|
305
|
+
* Doesn't count bans nested inside other formats/rules.
|
|
306
|
+
*/
|
|
307
|
+
private hasDirectCustomBanlistChanges() {
|
|
308
|
+
if (!this.format.customRules) return false;
|
|
309
|
+
for (const rule of this.format.customRules) {
|
|
310
|
+
for (const banlistOperator of ['-', '+', '*']) {
|
|
311
|
+
if (rule.startsWith(banlistOperator)) return true;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Inform user when custom bans are unsupported in a team generator.
|
|
319
|
+
*/
|
|
320
|
+
protected enforceNoDirectCustomBanlistChanges() {
|
|
321
|
+
if (this.hasDirectCustomBanlistChanges()) {
|
|
322
|
+
throw new Error(`Custom bans are not currently supported in ${this.format.name}.`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Inform user when complex bans are unsupported in a team generator.
|
|
328
|
+
*/
|
|
329
|
+
protected enforceNoDirectComplexBans() {
|
|
330
|
+
if (!this.format.customRules) return false;
|
|
331
|
+
for (const rule of this.format.customRules) {
|
|
332
|
+
if (rule.includes('+') && !rule.startsWith('+')) {
|
|
333
|
+
throw new Error(`Complex bans are not currently supported in ${this.format.name}.`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Validate set element pool size is sufficient to support size requirements after simple bans.
|
|
340
|
+
*/
|
|
341
|
+
private enforceCustomPoolSizeNoComplexBans(
|
|
342
|
+
effectTypeName: string,
|
|
343
|
+
basicEffectPool: BasicEffect[],
|
|
344
|
+
requiredCount: number,
|
|
345
|
+
requiredCountExplanation: string
|
|
346
|
+
) {
|
|
347
|
+
if (basicEffectPool.length >= requiredCount) return;
|
|
348
|
+
throw new Error(`Legal ${effectTypeName} count is insufficient to support ${requiredCountExplanation} (${basicEffectPool.length} / ${requiredCount}).`);
|
|
349
|
+
}
|
|
350
|
+
|
|
293
351
|
unrejectableMovesInSingles(move: Move) {
|
|
294
352
|
// These moves cannot be rejected in favor of a forced move in singles
|
|
295
353
|
return (move.category !== 'Status' || !move.flags.heal) && ![
|
|
@@ -304,6 +362,8 @@ export class RandomTeams {
|
|
|
304
362
|
}
|
|
305
363
|
|
|
306
364
|
randomCCTeam(): RandomTeamsTypes.RandomSet[] {
|
|
365
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
366
|
+
|
|
307
367
|
const dex = this.dex;
|
|
308
368
|
const team = [];
|
|
309
369
|
|
|
@@ -463,7 +523,7 @@ export class RandomTeams {
|
|
|
463
523
|
return team;
|
|
464
524
|
}
|
|
465
525
|
|
|
466
|
-
randomNPokemon(n: number, requiredType?: string, minSourceGen?: number) {
|
|
526
|
+
randomNPokemon(n: number, requiredType?: string, minSourceGen?: number, ruleTable?: Dex.RuleTable) {
|
|
467
527
|
// Picks `n` random pokemon--no repeats, even among formes
|
|
468
528
|
// Also need to either normalize for formes or select formes at random
|
|
469
529
|
// Unreleased are okay but no CAP
|
|
@@ -474,15 +534,65 @@ export class RandomTeams {
|
|
|
474
534
|
throw new Error(`"${requiredType}" is not a valid type.`);
|
|
475
535
|
}
|
|
476
536
|
|
|
537
|
+
const isNotCustom = !ruleTable;
|
|
538
|
+
|
|
477
539
|
const pool: number[] = [];
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
540
|
+
let speciesPool: Species[] = [];
|
|
541
|
+
if (isNotCustom) {
|
|
542
|
+
speciesPool = [...this.dex.species.all()];
|
|
543
|
+
for (const species of speciesPool) {
|
|
544
|
+
if (species.isNonstandard && species.isNonstandard !== 'Unobtainable') continue;
|
|
545
|
+
if (requiredType && !species.types.includes(requiredType)) continue;
|
|
546
|
+
if (minSourceGen && species.gen < minSourceGen) continue;
|
|
547
|
+
const num = species.num;
|
|
548
|
+
if (num <= 0 || pool.includes(num)) continue;
|
|
549
|
+
if (num > last) break;
|
|
550
|
+
pool.push(num);
|
|
551
|
+
}
|
|
552
|
+
} else {
|
|
553
|
+
const EXISTENCE_TAG = ['past', 'future', 'lgpe', 'unobtainable', 'cap', 'custom', 'nonexistent'];
|
|
554
|
+
const nonexistentBanReason = ruleTable.check('nonexistent');
|
|
555
|
+
// Assume tierSpecies does not differ from species here (mega formes can be used without their stone, etc)
|
|
556
|
+
for (const species of this.dex.species.all()) {
|
|
557
|
+
if (requiredType && !species.types.includes(requiredType)) continue;
|
|
558
|
+
|
|
559
|
+
let banReason = ruleTable.check('pokemon:' + species.id);
|
|
560
|
+
if (banReason) continue;
|
|
561
|
+
if (banReason !== '') {
|
|
562
|
+
if (species.isMega && ruleTable.check('pokemontag:mega')) continue;
|
|
563
|
+
|
|
564
|
+
banReason = ruleTable.check('basepokemon:' + toID(species.baseSpecies));
|
|
565
|
+
if (banReason) continue;
|
|
566
|
+
if (banReason !== '' || this.dex.species.get(species.baseSpecies).isNonstandard === species.isNonstandard) {
|
|
567
|
+
const nonexistentCheck = Tags.nonexistent.genericFilter!(species) && nonexistentBanReason;
|
|
568
|
+
let tagWhitelisted = false;
|
|
569
|
+
let tagBlacklisted = false;
|
|
570
|
+
for (const ruleid of ruleTable.tagRules) {
|
|
571
|
+
if (ruleid.startsWith('*')) continue;
|
|
572
|
+
const tagid = ruleid.slice(12);
|
|
573
|
+
const tag = Tags[tagid];
|
|
574
|
+
if ((tag.speciesFilter || tag.genericFilter)!(species)) {
|
|
575
|
+
const existenceTag = EXISTENCE_TAG.includes(tagid);
|
|
576
|
+
if (ruleid.startsWith('+')) {
|
|
577
|
+
if (!existenceTag && nonexistentCheck) continue;
|
|
578
|
+
tagWhitelisted = true;
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
tagBlacklisted = true;
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (tagBlacklisted) continue;
|
|
586
|
+
if (!tagWhitelisted) {
|
|
587
|
+
if (ruleTable.check('pokemontag:allpokemon')) continue;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const num = species.num;
|
|
592
|
+
if (pool.includes(num)) continue;
|
|
593
|
+
pool.push(num);
|
|
594
|
+
speciesPool.push(species);
|
|
595
|
+
}
|
|
486
596
|
}
|
|
487
597
|
|
|
488
598
|
const hasDexNumber: {[k: string]: number} = {};
|
|
@@ -492,12 +602,16 @@ export class RandomTeams {
|
|
|
492
602
|
}
|
|
493
603
|
|
|
494
604
|
const formes: string[][] = [];
|
|
495
|
-
for (const species of
|
|
605
|
+
for (const species of speciesPool) {
|
|
496
606
|
if (!(species.num in hasDexNumber)) continue;
|
|
497
|
-
if (species.gen
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
607
|
+
if (isNotCustom && (species.gen > this.gen ||
|
|
608
|
+
(species.isNonstandard && species.isNonstandard !== 'Unobtainable'))) continue;
|
|
609
|
+
if (!formes[hasDexNumber[species.num]]) formes[hasDexNumber[species.num]] = [];
|
|
610
|
+
formes[hasDexNumber[species.num]].push(species.name);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (formes.length < n) {
|
|
614
|
+
throw new Error(`Legal Pokemon forme count insufficient to support Max Team Size: (${formes.length} / ${n}).`);
|
|
501
615
|
}
|
|
502
616
|
|
|
503
617
|
const nPokemon = [];
|
|
@@ -511,15 +625,138 @@ export class RandomTeams {
|
|
|
511
625
|
}
|
|
512
626
|
|
|
513
627
|
randomHCTeam(): PokemonSet[] {
|
|
514
|
-
const
|
|
628
|
+
const hasCustomBans = this.hasDirectCustomBanlistChanges();
|
|
629
|
+
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
630
|
+
const hasNonexistentBan = hasCustomBans && ruleTable.check('nonexistent');
|
|
631
|
+
const hasNonexistentWhitelist = hasCustomBans && (hasNonexistentBan === '');
|
|
515
632
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const naturePool = this.dex.natures.all();
|
|
633
|
+
if (hasCustomBans) {
|
|
634
|
+
this.enforceNoDirectComplexBans();
|
|
635
|
+
}
|
|
520
636
|
|
|
521
|
-
|
|
637
|
+
// Item Pool
|
|
638
|
+
const doItemsExist = this.gen > 1;
|
|
639
|
+
let itemPool: Item[] = [];
|
|
640
|
+
if (doItemsExist) {
|
|
641
|
+
if (!hasCustomBans) {
|
|
642
|
+
itemPool = [...this.dex.items.all()].filter(item => (item.gen <= this.gen && !item.isNonstandard));
|
|
643
|
+
} else {
|
|
644
|
+
const hasAllItemsBan = ruleTable.check('pokemontag:allitems');
|
|
645
|
+
for (const item of this.dex.items.all()) {
|
|
646
|
+
let banReason = ruleTable.check('item:' + item.id);
|
|
647
|
+
if (banReason) continue;
|
|
648
|
+
if (banReason !== '' && item.id) {
|
|
649
|
+
if (hasAllItemsBan) continue;
|
|
650
|
+
if (item.isNonstandard) {
|
|
651
|
+
banReason = ruleTable.check('pokemontag:' + toID(item.isNonstandard));
|
|
652
|
+
if (banReason) continue;
|
|
653
|
+
if (banReason !== '' && item.isNonstandard !== 'Unobtainable') {
|
|
654
|
+
if (hasNonexistentBan) continue;
|
|
655
|
+
if (!hasNonexistentWhitelist) continue;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
itemPool.push(item);
|
|
660
|
+
}
|
|
661
|
+
if (ruleTable.check('item:noitem')) {
|
|
662
|
+
this.enforceCustomPoolSizeNoComplexBans('item', itemPool, this.maxTeamSize, 'Max Team Size');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Ability Pool
|
|
668
|
+
const doAbilitiesExist = (this.gen > 2) && (this.dex.currentMod !== 'gen7letsgo');
|
|
669
|
+
let abilityPool: Ability[] = [];
|
|
670
|
+
if (doAbilitiesExist) {
|
|
671
|
+
if (!hasCustomBans) {
|
|
672
|
+
abilityPool = [...this.dex.abilities.all()].filter(ability => (ability.gen <= this.gen && !ability.isNonstandard));
|
|
673
|
+
} else {
|
|
674
|
+
const hasAllAbilitiesBan = ruleTable.check('pokemontag:allabilities');
|
|
675
|
+
for (const ability of this.dex.abilities.all()) {
|
|
676
|
+
let banReason = ruleTable.check('ability:' + ability.id);
|
|
677
|
+
if (banReason) continue;
|
|
678
|
+
if (banReason !== '') {
|
|
679
|
+
if (hasAllAbilitiesBan) continue;
|
|
680
|
+
if (ability.isNonstandard) {
|
|
681
|
+
banReason = ruleTable.check('pokemontag:' + toID(ability.isNonstandard));
|
|
682
|
+
if (banReason) continue;
|
|
683
|
+
if (banReason !== '') {
|
|
684
|
+
if (hasNonexistentBan) continue;
|
|
685
|
+
if (!hasNonexistentWhitelist) continue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
abilityPool.push(ability);
|
|
690
|
+
}
|
|
691
|
+
if (ruleTable.check('ability:noability')) {
|
|
692
|
+
this.enforceCustomPoolSizeNoComplexBans('ability', abilityPool, this.maxTeamSize, 'Max Team Size');
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Move Pool
|
|
698
|
+
const setMoveCount = ruleTable.maxMoveCount;
|
|
699
|
+
let movePool: Move[] = [];
|
|
700
|
+
if (!hasCustomBans) {
|
|
701
|
+
movePool = [...this.dex.moves.all()].filter(move =>
|
|
702
|
+
(move.gen <= this.gen && !move.isNonstandard && !move.name.startsWith('Hidden Power ')));
|
|
703
|
+
} else {
|
|
704
|
+
const hasAllMovesBan = ruleTable.check('pokemontag:allmoves');
|
|
705
|
+
for (const move of this.dex.moves.all()) {
|
|
706
|
+
// Legality of specific HP types can't be altered in built formats anyway
|
|
707
|
+
if (move.name.startsWith('Hidden Power ')) continue;
|
|
708
|
+
let banReason = ruleTable.check('move:' + move.id);
|
|
709
|
+
if (banReason) continue;
|
|
710
|
+
if (banReason !== '') {
|
|
711
|
+
if (hasAllMovesBan) continue;
|
|
712
|
+
if (move.isNonstandard) {
|
|
713
|
+
banReason = ruleTable.check('pokemontag:' + toID(move.isNonstandard));
|
|
714
|
+
if (banReason) continue;
|
|
715
|
+
if (banReason !== '' && move.isNonstandard !== 'Unobtainable') {
|
|
716
|
+
if (hasNonexistentBan) continue;
|
|
717
|
+
if (!hasNonexistentWhitelist) continue;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
movePool.push(move);
|
|
722
|
+
}
|
|
723
|
+
this.enforceCustomPoolSizeNoComplexBans('move', movePool, this.maxTeamSize * setMoveCount, 'Max Team Size * Max Move Count');
|
|
724
|
+
}
|
|
522
725
|
|
|
726
|
+
// Nature Pool
|
|
727
|
+
const doNaturesExist = this.gen > 2;
|
|
728
|
+
let naturePool: Nature[] = [];
|
|
729
|
+
if (doNaturesExist) {
|
|
730
|
+
if (!hasCustomBans) {
|
|
731
|
+
if (!hasCustomBans) {
|
|
732
|
+
naturePool = [...this.dex.natures.all()];
|
|
733
|
+
} else {
|
|
734
|
+
const hasAllNaturesBan = ruleTable.check('pokemontag:allnatures');
|
|
735
|
+
for (const nature of this.dex.natures.all()) {
|
|
736
|
+
let banReason = ruleTable.check('nature:' + nature.id);
|
|
737
|
+
if (banReason) continue;
|
|
738
|
+
if (banReason !== '' && nature.id) {
|
|
739
|
+
if (hasAllNaturesBan) continue;
|
|
740
|
+
if (nature.isNonstandard) {
|
|
741
|
+
banReason = ruleTable.check('pokemontag:' + toID(nature.isNonstandard));
|
|
742
|
+
if (banReason) continue;
|
|
743
|
+
if (banReason !== '' && nature.isNonstandard !== 'Unobtainable') {
|
|
744
|
+
if (hasNonexistentBan) continue;
|
|
745
|
+
if (!hasNonexistentWhitelist) continue;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
naturePool.push(nature);
|
|
750
|
+
}
|
|
751
|
+
// There is no 'nature:nonature' rule so do not constrain pool size
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype, undefined,
|
|
757
|
+
hasCustomBans ? ruleTable : undefined);
|
|
758
|
+
|
|
759
|
+
const team = [];
|
|
523
760
|
for (const forme of randomN) {
|
|
524
761
|
// Choose forme
|
|
525
762
|
const species = this.dex.species.get(forme);
|
|
@@ -527,31 +764,25 @@ export class RandomTeams {
|
|
|
527
764
|
// Random unique item
|
|
528
765
|
let item = '';
|
|
529
766
|
let itemData;
|
|
530
|
-
if (
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
item = itemData.name;
|
|
534
|
-
} while (itemData.gen > this.gen || itemData.isNonstandard);
|
|
767
|
+
if (doItemsExist) {
|
|
768
|
+
itemData = this.sampleNoReplace(itemPool);
|
|
769
|
+
item = itemData?.name;
|
|
535
770
|
}
|
|
536
771
|
|
|
537
772
|
// Random unique ability
|
|
538
773
|
let ability = 'No Ability';
|
|
539
774
|
let abilityData;
|
|
540
|
-
if (
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
ability = abilityData.name;
|
|
544
|
-
} while (abilityData.gen > this.gen || abilityData.isNonstandard);
|
|
775
|
+
if (doAbilitiesExist) {
|
|
776
|
+
abilityData = this.sampleNoReplace(abilityPool);
|
|
777
|
+
ability = abilityData?.name;
|
|
545
778
|
}
|
|
546
779
|
|
|
547
780
|
// Random unique moves
|
|
548
781
|
const m = [];
|
|
549
782
|
do {
|
|
550
783
|
const move = this.sampleNoReplace(movePool);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
} while (m.length < 4);
|
|
784
|
+
m.push(move.id);
|
|
785
|
+
} while (m.length < setMoveCount);
|
|
555
786
|
|
|
556
787
|
// Random EVs
|
|
557
788
|
const evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
|
@@ -580,7 +811,10 @@ export class RandomTeams {
|
|
|
580
811
|
};
|
|
581
812
|
|
|
582
813
|
// Random nature
|
|
583
|
-
|
|
814
|
+
let nature = '';
|
|
815
|
+
if (doNaturesExist && (naturePool.length > 0)) {
|
|
816
|
+
nature = this.sample(naturePool).name;
|
|
817
|
+
}
|
|
584
818
|
|
|
585
819
|
// Level balance
|
|
586
820
|
const mbstmin = 1307;
|
|
@@ -697,7 +931,7 @@ export class RandomTeams {
|
|
|
697
931
|
// Moves with secondary effects:
|
|
698
932
|
if (move.secondary) {
|
|
699
933
|
counter.add('sheerforce');
|
|
700
|
-
if (move
|
|
934
|
+
if (sereneGraceBenefits(move)) {
|
|
701
935
|
counter.add('serenegrace');
|
|
702
936
|
}
|
|
703
937
|
}
|
|
@@ -1086,10 +1320,6 @@ export class RandomTeams {
|
|
|
1086
1320
|
case 'airslash':
|
|
1087
1321
|
return {cull:
|
|
1088
1322
|
(species.id === 'naganadel' && moves.has('nastyplot')) ||
|
|
1089
|
-
// I'm told that Nasty Plot Noctowl wants Air Slash (presumably for consistent damage),
|
|
1090
|
-
// and Defog Noctowl wants Hurricane—presumably for a high-risk, high-reward damaging move
|
|
1091
|
-
// after its main job of removing hazards is done.
|
|
1092
|
-
(species.id === 'noctowl' && !counter.setupType) ||
|
|
1093
1323
|
hasRestTalk ||
|
|
1094
1324
|
(abilities.has('Simple') && !!counter.get('recovery')) ||
|
|
1095
1325
|
counter.setupType === 'Physical',
|
|
@@ -1098,9 +1328,7 @@ export class RandomTeams {
|
|
|
1098
1328
|
// Special case for Mew, which only wants Brave Bird with Swords Dance
|
|
1099
1329
|
return {cull: moves.has('dragondance')};
|
|
1100
1330
|
case 'hurricane':
|
|
1101
|
-
|
|
1102
|
-
const noctowlCase = (!isNoDynamax && !isDoubles && species.id === 'noctowl' && !!counter.setupType);
|
|
1103
|
-
return {cull: counter.setupType === 'Physical' || noctowlCase};
|
|
1331
|
+
return {cull: counter.setupType === 'Physical'};
|
|
1104
1332
|
case 'futuresight':
|
|
1105
1333
|
return {cull: moves.has('psyshock') || moves.has('trick') || movePool.includes('teleport')};
|
|
1106
1334
|
case 'photongeyser':
|
|
@@ -1785,7 +2013,6 @@ export class RandomTeams {
|
|
|
1785
2013
|
// Iterate through the moves again, this time to cull them:
|
|
1786
2014
|
for (const moveid of moves) {
|
|
1787
2015
|
const move = this.dex.moves.get(moveid);
|
|
1788
|
-
|
|
1789
2016
|
let {cull, isSetup} = this.shouldCullMove(
|
|
1790
2017
|
move, types, moves, abilities, counter,
|
|
1791
2018
|
movePool, teamDetails, species, isLead, isDoubles, isNoDynamax
|
|
@@ -1804,7 +2031,7 @@ export class RandomTeams {
|
|
|
1804
2031
|
const isLowBP = move.basePower && move.basePower < 50;
|
|
1805
2032
|
|
|
1806
2033
|
// Genesect-Douse should never reject Techno Blast
|
|
1807
|
-
const moveIsRejectable =
|
|
2034
|
+
const moveIsRejectable = !(species.id === 'genesectdouse' && move.id === 'technoblast') && (
|
|
1808
2035
|
move.category === 'Status' ||
|
|
1809
2036
|
!types.has(move.type) ||
|
|
1810
2037
|
(isLowBP && !move.multihit && !abilities.has('Technician'))
|
|
@@ -1816,7 +2043,6 @@ export class RandomTeams {
|
|
|
1816
2043
|
(counter.get(counter.setupType) + counter.get('Status') > 3 && !counter.get('hazards')) ||
|
|
1817
2044
|
(move.category !== counter.setupType && move.category !== 'Status')
|
|
1818
2045
|
);
|
|
1819
|
-
|
|
1820
2046
|
if (moveIsRejectable && (
|
|
1821
2047
|
!cull && !isSetup && !move.weather && !move.stallingMove && notImportantSetup && !move.damage &&
|
|
1822
2048
|
(isDoubles ? this.unrejectableMovesInDoubles(move) : this.unrejectableMovesInSingles(move))
|
|
@@ -1838,7 +2064,8 @@ export class RandomTeams {
|
|
|
1838
2064
|
) {
|
|
1839
2065
|
cull = true;
|
|
1840
2066
|
// Pokemon should have moves that benefit their typing
|
|
1841
|
-
|
|
2067
|
+
// Don't cull Sticky Web in type-based enforcement, and make sure Azumarill always has Aqua Jet
|
|
2068
|
+
} else if (move.id !== 'stickyweb' && !(species.id === 'azumarill' && move.id === 'aquajet')) {
|
|
1842
2069
|
for (const type of types) {
|
|
1843
2070
|
if (runEnforcementChecker(type)) {
|
|
1844
2071
|
cull = true;
|
|
@@ -1859,6 +2086,7 @@ export class RandomTeams {
|
|
|
1859
2086
|
}
|
|
1860
2087
|
}
|
|
1861
2088
|
|
|
2089
|
+
|
|
1862
2090
|
// Remove rejected moves from the move list
|
|
1863
2091
|
if (cull && movePool.length) {
|
|
1864
2092
|
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
|
|
@@ -2129,6 +2357,8 @@ export class RandomTeams {
|
|
|
2129
2357
|
}
|
|
2130
2358
|
|
|
2131
2359
|
randomTeam() {
|
|
2360
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2361
|
+
|
|
2132
2362
|
const seed = this.prng.seed;
|
|
2133
2363
|
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
2134
2364
|
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
|
@@ -2301,6 +2531,8 @@ export class RandomTeams {
|
|
|
2301
2531
|
randomCAP1v1Sets: AnyObject = {};
|
|
2302
2532
|
|
|
2303
2533
|
randomCAP1v1Team() {
|
|
2534
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2535
|
+
|
|
2304
2536
|
const pokemon = [];
|
|
2305
2537
|
const pokemonPool = Object.keys(this.randomCAP1v1Sets);
|
|
2306
2538
|
|
|
@@ -2404,6 +2636,8 @@ export class RandomTeams {
|
|
|
2404
2636
|
}
|
|
2405
2637
|
|
|
2406
2638
|
randomBSSFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] {
|
|
2639
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2640
|
+
|
|
2407
2641
|
const forceResult = (depth >= 4);
|
|
2408
2642
|
|
|
2409
2643
|
const pokemon = [];
|