@pkmn/randoms 0.5.3 → 0.5.7
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 +5 -1
- package/build/gen5.js.map +1 -1
- package/build/gen6.js +2 -1
- package/build/gen6.js.map +1 -1
- package/build/gen7.js +7 -2
- package/build/gen7.js.map +1 -1
- package/build/gen8.d.ts +19 -2
- package/build/gen8.js +301 -53
- 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 +6 -1
- package/src/gen6.ts +3 -1
- package/src/gen7.ts +12 -2
- package/src/gen8.ts +284 -50
package/build/gen8.js
CHANGED
|
@@ -49,6 +49,10 @@ const NoStab = [
|
|
|
49
49
|
const Hazards = [
|
|
50
50
|
'spikes', 'stealthrock', 'stickyweb', 'toxicspikes',
|
|
51
51
|
];
|
|
52
|
+
function sereneGraceBenefits(move) {
|
|
53
|
+
var _a;
|
|
54
|
+
return ((_a = move.secondary) === null || _a === void 0 ? void 0 : _a.chance) && move.secondary.chance >= 20 && move.secondary.chance < 100;
|
|
55
|
+
}
|
|
52
56
|
class RandomTeams {
|
|
53
57
|
constructor(dex, format, prng) {
|
|
54
58
|
this.randomCAP1v1Sets = {};
|
|
@@ -206,6 +210,8 @@ class RandomTeams {
|
|
|
206
210
|
*/
|
|
207
211
|
sampleNoReplace(list) {
|
|
208
212
|
const length = list.length;
|
|
213
|
+
if (length === 0)
|
|
214
|
+
return null;
|
|
209
215
|
const index = this.random(length);
|
|
210
216
|
return this.fastPop(list, index);
|
|
211
217
|
}
|
|
@@ -221,6 +227,49 @@ class RandomTeams {
|
|
|
221
227
|
}
|
|
222
228
|
return samples;
|
|
223
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Check if user has directly tried to ban/unban/restrict things in a custom battle.
|
|
232
|
+
* Doesn't count bans nested inside other formats/rules.
|
|
233
|
+
*/
|
|
234
|
+
hasDirectCustomBanlistChanges() {
|
|
235
|
+
if (!this.format.customRules)
|
|
236
|
+
return false;
|
|
237
|
+
for (const rule of this.format.customRules) {
|
|
238
|
+
for (const banlistOperator of ['-', '+', '*']) {
|
|
239
|
+
if (rule.startsWith(banlistOperator))
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Inform user when custom bans are unsupported in a team generator.
|
|
247
|
+
*/
|
|
248
|
+
enforceNoDirectCustomBanlistChanges() {
|
|
249
|
+
if (this.hasDirectCustomBanlistChanges()) {
|
|
250
|
+
throw new Error(`Custom bans are not currently supported in ${this.format.name}.`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Inform user when complex bans are unsupported in a team generator.
|
|
255
|
+
*/
|
|
256
|
+
enforceNoDirectComplexBans() {
|
|
257
|
+
if (!this.format.customRules)
|
|
258
|
+
return false;
|
|
259
|
+
for (const rule of this.format.customRules) {
|
|
260
|
+
if (rule.includes('+') && !rule.startsWith('+')) {
|
|
261
|
+
throw new Error(`Complex bans are not currently supported in ${this.format.name}.`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Validate set element pool size is sufficient to support size requirements after simple bans.
|
|
267
|
+
*/
|
|
268
|
+
enforceCustomPoolSizeNoComplexBans(effectTypeName, basicEffectPool, requiredCount, requiredCountExplanation) {
|
|
269
|
+
if (basicEffectPool.length >= requiredCount)
|
|
270
|
+
return;
|
|
271
|
+
throw new Error(`Legal ${effectTypeName} count is insufficient to support ${requiredCountExplanation} (${basicEffectPool.length} / ${requiredCount}).`);
|
|
272
|
+
}
|
|
224
273
|
unrejectableMovesInSingles(move) {
|
|
225
274
|
// These moves cannot be rejected in favor of a forced move in singles
|
|
226
275
|
return (move.category !== 'Status' || !move.flags.heal) && ![
|
|
@@ -233,6 +282,7 @@ class RandomTeams {
|
|
|
233
282
|
return move.id !== 'bodypress';
|
|
234
283
|
}
|
|
235
284
|
randomCCTeam() {
|
|
285
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
236
286
|
const dex = this.dex;
|
|
237
287
|
const team = [];
|
|
238
288
|
const natures = this.dex.natures.all();
|
|
@@ -370,7 +420,7 @@ class RandomTeams {
|
|
|
370
420
|
}
|
|
371
421
|
return team;
|
|
372
422
|
}
|
|
373
|
-
randomNPokemon(n, requiredType, minSourceGen) {
|
|
423
|
+
randomNPokemon(n, requiredType, minSourceGen, ruleTable) {
|
|
374
424
|
// Picks `n` random pokemon--no repeats, even among formes
|
|
375
425
|
// Also need to either normalize for formes or select formes at random
|
|
376
426
|
// Unreleased are okay but no CAP
|
|
@@ -380,20 +430,77 @@ class RandomTeams {
|
|
|
380
430
|
if (requiredType && !this.dex.types.get(requiredType).exists) {
|
|
381
431
|
throw new Error(`"${requiredType}" is not a valid type.`);
|
|
382
432
|
}
|
|
433
|
+
const isNotCustom = !ruleTable;
|
|
383
434
|
const pool = [];
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
435
|
+
let speciesPool = [];
|
|
436
|
+
if (isNotCustom) {
|
|
437
|
+
speciesPool = [...this.dex.species.all()];
|
|
438
|
+
for (const species of speciesPool) {
|
|
439
|
+
if (species.isNonstandard && species.isNonstandard !== 'Unobtainable')
|
|
440
|
+
continue;
|
|
441
|
+
if (requiredType && !species.types.includes(requiredType))
|
|
442
|
+
continue;
|
|
443
|
+
if (minSourceGen && species.gen < minSourceGen)
|
|
444
|
+
continue;
|
|
445
|
+
const num = species.num;
|
|
446
|
+
if (num <= 0 || pool.includes(num))
|
|
447
|
+
continue;
|
|
448
|
+
if (num > last)
|
|
449
|
+
break;
|
|
450
|
+
pool.push(num);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
const EXISTENCE_TAG = ['past', 'future', 'lgpe', 'unobtainable', 'cap', 'custom', 'nonexistent'];
|
|
455
|
+
const nonexistentBanReason = ruleTable.check('nonexistent');
|
|
456
|
+
// Assume tierSpecies does not differ from species here (mega formes can be used without their stone, etc)
|
|
457
|
+
for (const species of this.dex.species.all()) {
|
|
458
|
+
if (requiredType && !species.types.includes(requiredType))
|
|
459
|
+
continue;
|
|
460
|
+
let banReason = ruleTable.check('pokemon:' + species.id);
|
|
461
|
+
if (banReason)
|
|
462
|
+
continue;
|
|
463
|
+
if (banReason !== '') {
|
|
464
|
+
if (species.isMega && ruleTable.check('pokemontag:mega'))
|
|
465
|
+
continue;
|
|
466
|
+
banReason = ruleTable.check('basepokemon:' + (0, sim_1.toID)(species.baseSpecies));
|
|
467
|
+
if (banReason)
|
|
468
|
+
continue;
|
|
469
|
+
if (banReason !== '' || this.dex.species.get(species.baseSpecies).isNonstandard === species.isNonstandard) {
|
|
470
|
+
const nonexistentCheck = sim_1.Tags.nonexistent.genericFilter(species) && nonexistentBanReason;
|
|
471
|
+
let tagWhitelisted = false;
|
|
472
|
+
let tagBlacklisted = false;
|
|
473
|
+
for (const ruleid of ruleTable.tagRules) {
|
|
474
|
+
if (ruleid.startsWith('*'))
|
|
475
|
+
continue;
|
|
476
|
+
const tagid = ruleid.slice(12);
|
|
477
|
+
const tag = sim_1.Tags[tagid];
|
|
478
|
+
if ((tag.speciesFilter || tag.genericFilter)(species)) {
|
|
479
|
+
const existenceTag = EXISTENCE_TAG.includes(tagid);
|
|
480
|
+
if (ruleid.startsWith('+')) {
|
|
481
|
+
if (!existenceTag && nonexistentCheck)
|
|
482
|
+
continue;
|
|
483
|
+
tagWhitelisted = true;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
tagBlacklisted = true;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (tagBlacklisted)
|
|
491
|
+
continue;
|
|
492
|
+
if (!tagWhitelisted) {
|
|
493
|
+
if (ruleTable.check('pokemontag:allpokemon'))
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const num = species.num;
|
|
499
|
+
if (pool.includes(num))
|
|
500
|
+
continue;
|
|
501
|
+
pool.push(num);
|
|
502
|
+
speciesPool.push(species);
|
|
503
|
+
}
|
|
397
504
|
}
|
|
398
505
|
const hasDexNumber = {};
|
|
399
506
|
for (let i = 0; i < n; i++) {
|
|
@@ -401,14 +508,18 @@ class RandomTeams {
|
|
|
401
508
|
hasDexNumber[num] = i;
|
|
402
509
|
}
|
|
403
510
|
const formes = [];
|
|
404
|
-
for (const species of
|
|
511
|
+
for (const species of speciesPool) {
|
|
405
512
|
if (!(species.num in hasDexNumber))
|
|
406
513
|
continue;
|
|
407
|
-
if (species.gen
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
514
|
+
if (isNotCustom && (species.gen > this.gen ||
|
|
515
|
+
(species.isNonstandard && species.isNonstandard !== 'Unobtainable')))
|
|
516
|
+
continue;
|
|
517
|
+
if (!formes[hasDexNumber[species.num]])
|
|
518
|
+
formes[hasDexNumber[species.num]] = [];
|
|
519
|
+
formes[hasDexNumber[species.num]].push(species.name);
|
|
520
|
+
}
|
|
521
|
+
if (formes.length < n) {
|
|
522
|
+
throw new Error(`Legal Pokemon forme count insufficient to support Max Team Size: (${formes.length} / ${n}).`);
|
|
412
523
|
}
|
|
413
524
|
const nPokemon = [];
|
|
414
525
|
for (let i = 0; i < n; i++) {
|
|
@@ -420,41 +531,177 @@ class RandomTeams {
|
|
|
420
531
|
return nPokemon;
|
|
421
532
|
}
|
|
422
533
|
randomHCTeam() {
|
|
534
|
+
const hasCustomBans = this.hasDirectCustomBanlistChanges();
|
|
535
|
+
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
536
|
+
const hasNonexistentBan = hasCustomBans && ruleTable.check('nonexistent');
|
|
537
|
+
const hasNonexistentWhitelist = hasCustomBans && (hasNonexistentBan === '');
|
|
538
|
+
if (hasCustomBans) {
|
|
539
|
+
this.enforceNoDirectComplexBans();
|
|
540
|
+
}
|
|
541
|
+
// Item Pool
|
|
542
|
+
const doItemsExist = this.gen > 1;
|
|
543
|
+
let itemPool = [];
|
|
544
|
+
if (doItemsExist) {
|
|
545
|
+
if (!hasCustomBans) {
|
|
546
|
+
itemPool = [...this.dex.items.all()].filter(item => (item.gen <= this.gen && !item.isNonstandard));
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const hasAllItemsBan = ruleTable.check('pokemontag:allitems');
|
|
550
|
+
for (const item of this.dex.items.all()) {
|
|
551
|
+
let banReason = ruleTable.check('item:' + item.id);
|
|
552
|
+
if (banReason)
|
|
553
|
+
continue;
|
|
554
|
+
if (banReason !== '' && item.id) {
|
|
555
|
+
if (hasAllItemsBan)
|
|
556
|
+
continue;
|
|
557
|
+
if (item.isNonstandard) {
|
|
558
|
+
banReason = ruleTable.check('pokemontag:' + (0, sim_1.toID)(item.isNonstandard));
|
|
559
|
+
if (banReason)
|
|
560
|
+
continue;
|
|
561
|
+
if (banReason !== '' && item.isNonstandard !== 'Unobtainable') {
|
|
562
|
+
if (hasNonexistentBan)
|
|
563
|
+
continue;
|
|
564
|
+
if (!hasNonexistentWhitelist)
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
itemPool.push(item);
|
|
570
|
+
}
|
|
571
|
+
if (ruleTable.check('item:noitem')) {
|
|
572
|
+
this.enforceCustomPoolSizeNoComplexBans('item', itemPool, this.maxTeamSize, 'Max Team Size');
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// Ability Pool
|
|
577
|
+
const doAbilitiesExist = (this.gen > 2) && (this.dex.currentMod !== 'gen7letsgo');
|
|
578
|
+
let abilityPool = [];
|
|
579
|
+
if (doAbilitiesExist) {
|
|
580
|
+
if (!hasCustomBans) {
|
|
581
|
+
abilityPool = [...this.dex.abilities.all()].filter(ability => (ability.gen <= this.gen && !ability.isNonstandard));
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
const hasAllAbilitiesBan = ruleTable.check('pokemontag:allabilities');
|
|
585
|
+
for (const ability of this.dex.abilities.all()) {
|
|
586
|
+
let banReason = ruleTable.check('ability:' + ability.id);
|
|
587
|
+
if (banReason)
|
|
588
|
+
continue;
|
|
589
|
+
if (banReason !== '') {
|
|
590
|
+
if (hasAllAbilitiesBan)
|
|
591
|
+
continue;
|
|
592
|
+
if (ability.isNonstandard) {
|
|
593
|
+
banReason = ruleTable.check('pokemontag:' + (0, sim_1.toID)(ability.isNonstandard));
|
|
594
|
+
if (banReason)
|
|
595
|
+
continue;
|
|
596
|
+
if (banReason !== '') {
|
|
597
|
+
if (hasNonexistentBan)
|
|
598
|
+
continue;
|
|
599
|
+
if (!hasNonexistentWhitelist)
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
abilityPool.push(ability);
|
|
605
|
+
}
|
|
606
|
+
if (ruleTable.check('ability:noability')) {
|
|
607
|
+
this.enforceCustomPoolSizeNoComplexBans('ability', abilityPool, this.maxTeamSize, 'Max Team Size');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
// Move Pool
|
|
612
|
+
const setMoveCount = ruleTable.maxMoveCount;
|
|
613
|
+
let movePool = [];
|
|
614
|
+
if (!hasCustomBans) {
|
|
615
|
+
movePool = [...this.dex.moves.all()].filter(move => (move.gen <= this.gen && !move.isNonstandard && !move.name.startsWith('Hidden Power ')));
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
const hasAllMovesBan = ruleTable.check('pokemontag:allmoves');
|
|
619
|
+
for (const move of this.dex.moves.all()) {
|
|
620
|
+
// Legality of specific HP types can't be altered in built formats anyway
|
|
621
|
+
if (move.name.startsWith('Hidden Power '))
|
|
622
|
+
continue;
|
|
623
|
+
let banReason = ruleTable.check('move:' + move.id);
|
|
624
|
+
if (banReason)
|
|
625
|
+
continue;
|
|
626
|
+
if (banReason !== '') {
|
|
627
|
+
if (hasAllMovesBan)
|
|
628
|
+
continue;
|
|
629
|
+
if (move.isNonstandard) {
|
|
630
|
+
banReason = ruleTable.check('pokemontag:' + (0, sim_1.toID)(move.isNonstandard));
|
|
631
|
+
if (banReason)
|
|
632
|
+
continue;
|
|
633
|
+
if (banReason !== '' && move.isNonstandard !== 'Unobtainable') {
|
|
634
|
+
if (hasNonexistentBan)
|
|
635
|
+
continue;
|
|
636
|
+
if (!hasNonexistentWhitelist)
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
movePool.push(move);
|
|
642
|
+
}
|
|
643
|
+
this.enforceCustomPoolSizeNoComplexBans('move', movePool, this.maxTeamSize * setMoveCount, 'Max Team Size * Max Move Count');
|
|
644
|
+
}
|
|
645
|
+
// Nature Pool
|
|
646
|
+
const doNaturesExist = this.gen > 2;
|
|
647
|
+
let naturePool = [];
|
|
648
|
+
if (doNaturesExist) {
|
|
649
|
+
if (!hasCustomBans) {
|
|
650
|
+
if (!hasCustomBans) {
|
|
651
|
+
naturePool = [...this.dex.natures.all()];
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
const hasAllNaturesBan = ruleTable.check('pokemontag:allnatures');
|
|
655
|
+
for (const nature of this.dex.natures.all()) {
|
|
656
|
+
let banReason = ruleTable.check('nature:' + nature.id);
|
|
657
|
+
if (banReason)
|
|
658
|
+
continue;
|
|
659
|
+
if (banReason !== '' && nature.id) {
|
|
660
|
+
if (hasAllNaturesBan)
|
|
661
|
+
continue;
|
|
662
|
+
if (nature.isNonstandard) {
|
|
663
|
+
banReason = ruleTable.check('pokemontag:' + (0, sim_1.toID)(nature.isNonstandard));
|
|
664
|
+
if (banReason)
|
|
665
|
+
continue;
|
|
666
|
+
if (banReason !== '' && nature.isNonstandard !== 'Unobtainable') {
|
|
667
|
+
if (hasNonexistentBan)
|
|
668
|
+
continue;
|
|
669
|
+
if (!hasNonexistentWhitelist)
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
naturePool.push(nature);
|
|
675
|
+
}
|
|
676
|
+
// There is no 'nature:nonature' rule so do not constrain pool size
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype, undefined, hasCustomBans ? ruleTable : undefined);
|
|
423
681
|
const team = [];
|
|
424
|
-
const itemPool = [...this.dex.items.all()];
|
|
425
|
-
const abilityPool = [...this.dex.abilities.all()];
|
|
426
|
-
const movePool = [...this.dex.moves.all()];
|
|
427
|
-
const naturePool = this.dex.natures.all();
|
|
428
|
-
const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype);
|
|
429
682
|
for (const forme of randomN) {
|
|
430
683
|
// Choose forme
|
|
431
684
|
const species = this.dex.species.get(forme);
|
|
432
685
|
// Random unique item
|
|
433
686
|
let item = '';
|
|
434
687
|
let itemData;
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
item = itemData.name;
|
|
439
|
-
} while (itemData.gen > this.gen || itemData.isNonstandard);
|
|
688
|
+
if (doItemsExist) {
|
|
689
|
+
itemData = this.sampleNoReplace(itemPool);
|
|
690
|
+
item = itemData === null || itemData === void 0 ? void 0 : itemData.name;
|
|
440
691
|
}
|
|
441
692
|
// Random unique ability
|
|
442
693
|
let ability = 'No Ability';
|
|
443
694
|
let abilityData;
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
ability = abilityData.name;
|
|
448
|
-
} while (abilityData.gen > this.gen || abilityData.isNonstandard);
|
|
695
|
+
if (doAbilitiesExist) {
|
|
696
|
+
abilityData = this.sampleNoReplace(abilityPool);
|
|
697
|
+
ability = abilityData === null || abilityData === void 0 ? void 0 : abilityData.name;
|
|
449
698
|
}
|
|
450
699
|
// Random unique moves
|
|
451
700
|
const m = [];
|
|
452
701
|
do {
|
|
453
702
|
const move = this.sampleNoReplace(movePool);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
} while (m.length < 4);
|
|
703
|
+
m.push(move.id);
|
|
704
|
+
} while (m.length < setMoveCount);
|
|
458
705
|
// Random EVs
|
|
459
706
|
const evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
|
460
707
|
if (this.gen === 6) {
|
|
@@ -481,7 +728,10 @@ class RandomTeams {
|
|
|
481
728
|
spe: this.random(32),
|
|
482
729
|
};
|
|
483
730
|
// Random nature
|
|
484
|
-
|
|
731
|
+
let nature = '';
|
|
732
|
+
if (doNaturesExist && (naturePool.length > 0)) {
|
|
733
|
+
nature = this.sample(naturePool).name;
|
|
734
|
+
}
|
|
485
735
|
// Level balance
|
|
486
736
|
const mbstmin = 1307;
|
|
487
737
|
const stats = species.baseStats;
|
|
@@ -593,7 +843,7 @@ class RandomTeams {
|
|
|
593
843
|
// Moves with secondary effects:
|
|
594
844
|
if (move.secondary) {
|
|
595
845
|
counter.add('sheerforce');
|
|
596
|
-
if (move
|
|
846
|
+
if (sereneGraceBenefits(move)) {
|
|
597
847
|
counter.add('serenegrace');
|
|
598
848
|
}
|
|
599
849
|
}
|
|
@@ -990,10 +1240,6 @@ class RandomTeams {
|
|
|
990
1240
|
(moves.has('toxic') && movePool.includes('earthpower'))) };
|
|
991
1241
|
case 'airslash':
|
|
992
1242
|
return { cull: (species.id === 'naganadel' && moves.has('nastyplot')) ||
|
|
993
|
-
// I'm told that Nasty Plot Noctowl wants Air Slash (presumably for consistent damage),
|
|
994
|
-
// and Defog Noctowl wants Hurricane—presumably for a high-risk, high-reward damaging move
|
|
995
|
-
// after its main job of removing hazards is done.
|
|
996
|
-
(species.id === 'noctowl' && !counter.setupType) ||
|
|
997
1243
|
hasRestTalk ||
|
|
998
1244
|
(abilities.has('Simple') && !!counter.get('recovery')) ||
|
|
999
1245
|
counter.setupType === 'Physical',
|
|
@@ -1002,9 +1248,7 @@ class RandomTeams {
|
|
|
1002
1248
|
// Special case for Mew, which only wants Brave Bird with Swords Dance
|
|
1003
1249
|
return { cull: moves.has('dragondance') };
|
|
1004
1250
|
case 'hurricane':
|
|
1005
|
-
|
|
1006
|
-
const noctowlCase = (!isNoDynamax && !isDoubles && species.id === 'noctowl' && !!counter.setupType);
|
|
1007
|
-
return { cull: counter.setupType === 'Physical' || noctowlCase };
|
|
1251
|
+
return { cull: counter.setupType === 'Physical' };
|
|
1008
1252
|
case 'futuresight':
|
|
1009
1253
|
return { cull: moves.has('psyshock') || moves.has('trick') || movePool.includes('teleport') };
|
|
1010
1254
|
case 'photongeyser':
|
|
@@ -1361,7 +1605,7 @@ class RandomTeams {
|
|
|
1361
1605
|
const HDBBetterThanEviolite = (!isLead &&
|
|
1362
1606
|
this.dex.getEffectiveness('Rock', species) >= 2 &&
|
|
1363
1607
|
!isDoubles);
|
|
1364
|
-
if (species.
|
|
1608
|
+
if (species.nfe && !HDBBetterThanEviolite)
|
|
1365
1609
|
return 'Eviolite';
|
|
1366
1610
|
// Ability based logic and miscellaneous logic
|
|
1367
1611
|
if (species.name === 'Wobbuffet' || ['Cheek Pouch', 'Harvest', 'Ripen'].includes(ability))
|
|
@@ -1606,7 +1850,7 @@ class RandomTeams {
|
|
|
1606
1850
|
const isLowBP = move.basePower && move.basePower < 50;
|
|
1607
1851
|
// Genesect-Douse should never reject Techno Blast
|
|
1608
1852
|
const moveIsRejectable = !(species.id === 'genesectdouse' && move.id === 'technoblast') && (move.category === 'Status' ||
|
|
1609
|
-
!types.has(move.type) ||
|
|
1853
|
+
(!types.has(move.type) && move.id !== 'judgment') ||
|
|
1610
1854
|
(isLowBP && !move.multihit && !abilities.has('Technician')));
|
|
1611
1855
|
// Setup-supported moves should only be rejected under specific circumstances
|
|
1612
1856
|
const notImportantSetup = (!counter.setupType ||
|
|
@@ -1631,8 +1875,9 @@ class RandomTeams {
|
|
|
1631
1875
|
(moves.has('leechseed') && runEnforcementChecker('leechseed'))) {
|
|
1632
1876
|
cull = true;
|
|
1633
1877
|
// Pokemon should have moves that benefit their typing
|
|
1878
|
+
// Don't cull Sticky Web in type-based enforcement, and make sure Azumarill always has Aqua Jet
|
|
1634
1879
|
}
|
|
1635
|
-
else if (move.id !== 'stickyweb'
|
|
1880
|
+
else if (move.id !== 'stickyweb' && !(species.id === 'azumarill' && move.id === 'aquajet')) {
|
|
1636
1881
|
for (const type of types) {
|
|
1637
1882
|
if (runEnforcementChecker(type)) {
|
|
1638
1883
|
cull = true;
|
|
@@ -1838,7 +2083,7 @@ class RandomTeams {
|
|
|
1838
2083
|
PUBL: 87,
|
|
1839
2084
|
PU: 88, "(PU)": 88, NFE: 88,
|
|
1840
2085
|
};
|
|
1841
|
-
const customScale = {};
|
|
2086
|
+
const customScale = { delibird: 100, luvdisc: 100, spinda: 100, unown: 100 };
|
|
1842
2087
|
level = customScale[species.id] || tierScale[species.tier] || 80;
|
|
1843
2088
|
// Arbitrary levelling base on data files (typically winrate-influenced)
|
|
1844
2089
|
}
|
|
@@ -1885,7 +2130,7 @@ class RandomTeams {
|
|
|
1885
2130
|
const noAttackStatMoves = [...moves].every(m => {
|
|
1886
2131
|
const move = this.dex.moves.get(m);
|
|
1887
2132
|
if (move.damageCallback || move.damage)
|
|
1888
|
-
return
|
|
2133
|
+
return true;
|
|
1889
2134
|
return move.category !== 'Physical' || move.id === 'bodypress';
|
|
1890
2135
|
});
|
|
1891
2136
|
if (noAttackStatMoves && !moves.has('transform') && (!moves.has('shellsidearm') || !counter.get('Status'))) {
|
|
@@ -1936,6 +2181,7 @@ class RandomTeams {
|
|
|
1936
2181
|
}
|
|
1937
2182
|
randomTeam() {
|
|
1938
2183
|
var _a, _b;
|
|
2184
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
1939
2185
|
const seed = this.prng.seed;
|
|
1940
2186
|
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
1941
2187
|
const pokemon = [];
|
|
@@ -2125,6 +2371,7 @@ class RandomTeams {
|
|
|
2125
2371
|
return pokemon;
|
|
2126
2372
|
}
|
|
2127
2373
|
randomCAP1v1Team() {
|
|
2374
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2128
2375
|
const pokemon = [];
|
|
2129
2376
|
const pokemonPool = Object.keys(this.randomCAP1v1Sets);
|
|
2130
2377
|
while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
|
|
@@ -2221,6 +2468,7 @@ class RandomTeams {
|
|
|
2221
2468
|
}
|
|
2222
2469
|
randomBSSFactoryTeam(side, depth = 0) {
|
|
2223
2470
|
var _a;
|
|
2471
|
+
this.enforceNoDirectCustomBanlistChanges();
|
|
2224
2472
|
const forceResult = (depth >= 4);
|
|
2225
2473
|
const pokemon = [];
|
|
2226
2474
|
const pokemonPool = Object.keys(this.randomBSSFactorySets);
|