@pkmn/randoms 0.7.25 → 0.7.26
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/index.js +151 -24
- package/build/index.js.map +1 -1
- package/build/index.mjs +151 -24
- package/build/index.mjs.map +1 -1
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -300,6 +300,10 @@ var RandomGen8Teams = class {
|
|
|
300
300
|
random(m, n) {
|
|
301
301
|
return this.prng.next(m, n);
|
|
302
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Remove an element from an unsorted array significantly faster
|
|
305
|
+
* than .splice
|
|
306
|
+
*/
|
|
303
307
|
fastPop(list, index) {
|
|
304
308
|
const length = list.length;
|
|
305
309
|
if (index < 0 || index >= list.length) {
|
|
@@ -310,6 +314,10 @@ var RandomGen8Teams = class {
|
|
|
310
314
|
list.pop();
|
|
311
315
|
return element;
|
|
312
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Remove a random element from an unsorted array and return it.
|
|
319
|
+
* Uses the battle's RNG if in a battle.
|
|
320
|
+
*/
|
|
313
321
|
sampleNoReplace(list) {
|
|
314
322
|
const length = list.length;
|
|
315
323
|
if (length === 0)
|
|
@@ -317,6 +325,11 @@ var RandomGen8Teams = class {
|
|
|
317
325
|
const index = this.random(length);
|
|
318
326
|
return this.fastPop(list, index);
|
|
319
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Removes n random elements from an unsorted array and returns them.
|
|
330
|
+
* If n is less than the array's length, randomly removes and returns all the elements
|
|
331
|
+
* in the array (so the returned array could have length < n).
|
|
332
|
+
*/
|
|
320
333
|
multipleSamplesNoReplace(list, n) {
|
|
321
334
|
const samples = [];
|
|
322
335
|
while (samples.length < n && list.length) {
|
|
@@ -324,6 +337,10 @@ var RandomGen8Teams = class {
|
|
|
324
337
|
}
|
|
325
338
|
return samples;
|
|
326
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Check if user has directly tried to ban/unban/restrict things in a custom battle.
|
|
342
|
+
* Doesn't count bans nested inside other formats/rules.
|
|
343
|
+
*/
|
|
327
344
|
hasDirectCustomBanlistChanges() {
|
|
328
345
|
if (!this.format.customRules)
|
|
329
346
|
return false;
|
|
@@ -335,11 +352,17 @@ var RandomGen8Teams = class {
|
|
|
335
352
|
}
|
|
336
353
|
return false;
|
|
337
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Inform user when custom bans are unsupported in a team generator.
|
|
357
|
+
*/
|
|
338
358
|
enforceNoDirectCustomBanlistChanges() {
|
|
339
359
|
if (this.hasDirectCustomBanlistChanges()) {
|
|
340
360
|
throw new Error(`Custom bans are not currently supported in ${this.format.name}.`);
|
|
341
361
|
}
|
|
342
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Inform user when complex bans are unsupported in a team generator.
|
|
365
|
+
*/
|
|
343
366
|
enforceNoDirectComplexBans() {
|
|
344
367
|
if (!this.format.customRules)
|
|
345
368
|
return false;
|
|
@@ -349,6 +372,9 @@ var RandomGen8Teams = class {
|
|
|
349
372
|
}
|
|
350
373
|
}
|
|
351
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Validate set element pool size is sufficient to support size requirements after simple bans.
|
|
377
|
+
*/
|
|
352
378
|
enforceCustomPoolSizeNoComplexBans(effectTypeName, basicEffectPool, requiredCount, requiredCountExplanation) {
|
|
353
379
|
if (basicEffectPool.length >= requiredCount)
|
|
354
380
|
return;
|
|
@@ -884,7 +910,10 @@ var RandomGen8Teams = class {
|
|
|
884
910
|
counter.add("stab");
|
|
885
911
|
categories[move.category] += 0.1;
|
|
886
912
|
}
|
|
887
|
-
} else if (
|
|
913
|
+
} else if (
|
|
914
|
+
// Less obvious forms of STAB
|
|
915
|
+
moveType === "Normal" && ["Aerilate", "Galvanize", "Pixilate", "Refrigerate"].some((abil) => abilities.has(abil)) || move.priority === 0 && (abilities.has("Libero") || abilities.has("Protean")) && !this.noStab.includes(moveid) || moveType === "Steel" && abilities.has("Steelworker")
|
|
916
|
+
) {
|
|
888
917
|
counter.add("stab");
|
|
889
918
|
}
|
|
890
919
|
if (move.flags["bite"])
|
|
@@ -1169,7 +1198,10 @@ var RandomGen8Teams = class {
|
|
|
1169
1198
|
case "leafstorm":
|
|
1170
1199
|
const leafBladePossible = movePool.includes("leafblade") || moves.has("leafblade");
|
|
1171
1200
|
return {
|
|
1172
|
-
cull:
|
|
1201
|
+
cull: (
|
|
1202
|
+
// Virizion should always prefer Leaf Blade to Leaf Storm on Physical sets
|
|
1203
|
+
counter.setupType === "Physical" && (species.id === "virizion" || leafBladePossible) || moves.has("gigadrain") && !!counter.get("Status") || isDoubles && moves.has("energyball")
|
|
1204
|
+
)
|
|
1173
1205
|
};
|
|
1174
1206
|
case "powerwhip":
|
|
1175
1207
|
return { cull: moves.has("leechlife") };
|
|
@@ -1246,7 +1278,8 @@ var RandomGen8Teams = class {
|
|
|
1246
1278
|
return { cull: moves.has("knockoff") };
|
|
1247
1279
|
case "shadowball":
|
|
1248
1280
|
return {
|
|
1249
|
-
cull: isDoubles && moves.has("phantomforce") ||
|
|
1281
|
+
cull: isDoubles && moves.has("phantomforce") || // Special case for Sylveon, which never wants Shadow Ball as its only coverage move
|
|
1282
|
+
abilities.has("Pixilate") && (!!counter.setupType || counter.get("Status") > 1) || !types.has("Ghost") && movePool.includes("focusblast")
|
|
1250
1283
|
};
|
|
1251
1284
|
case "shadowclaw":
|
|
1252
1285
|
return { cull: types.has("Steel") && moves.has("shadowsneak") && counter.get("Physical") < 4 };
|
|
@@ -1259,7 +1292,10 @@ var RandomGen8Teams = class {
|
|
|
1259
1292
|
return { cull: pulseIncompatible && !shiftryCase && counter.setupType !== "Special" };
|
|
1260
1293
|
case "suckerpunch":
|
|
1261
1294
|
return {
|
|
1262
|
-
cull:
|
|
1295
|
+
cull: (
|
|
1296
|
+
// Shiftry in No Dynamax would otherwise get Choice Scarf Sucker Punch sometimes.
|
|
1297
|
+
isNoDynamax && species.id === "shiftry" && moves.has("defog") || moves.has("rest") || counter.damagingMoves.size < 2 || counter.setupType === "Special" || counter.get("Dark") > 1 && !types.has("Dark")
|
|
1298
|
+
)
|
|
1263
1299
|
};
|
|
1264
1300
|
case "dazzlinggleam":
|
|
1265
1301
|
return { cull: ["fleurcannon", "moonblast", "petaldance"].some((m) => moves.has(m)) };
|
|
@@ -1280,7 +1316,9 @@ var RandomGen8Teams = class {
|
|
|
1280
1316
|
return { cull: moves.has("rest") || moves.has("wish") || move.id === "synthesis" && moves.has("gigadrain") };
|
|
1281
1317
|
case "roost":
|
|
1282
1318
|
return {
|
|
1283
|
-
cull: moves.has("throatchop") ||
|
|
1319
|
+
cull: moves.has("throatchop") || // Hawlucha doesn't want Roost + 3 attacks
|
|
1320
|
+
moves.has("stoneedge") && species.id === "hawlucha" || // Special cases for Salamence, Dynaless Dragonite, and Scizor to help prevent sets with poor coverage or no setup.
|
|
1321
|
+
moves.has("dualwingbeat") && (moves.has("outrage") || species.id === "scizor")
|
|
1284
1322
|
};
|
|
1285
1323
|
case "reflect":
|
|
1286
1324
|
case "lightscreen":
|
|
@@ -1456,9 +1494,14 @@ var RandomGen8Teams = class {
|
|
|
1456
1494
|
case "Synchronize":
|
|
1457
1495
|
return counter.get("Status") < 3;
|
|
1458
1496
|
case "Technician":
|
|
1459
|
-
return !counter.get("technician") || moves.has("tailslap") || abilities.has("Punk Rock") ||
|
|
1497
|
+
return !counter.get("technician") || moves.has("tailslap") || abilities.has("Punk Rock") || // For Doubles Alolan Persian
|
|
1498
|
+
movePool.includes("snarl");
|
|
1460
1499
|
case "Tinted Lens":
|
|
1461
|
-
return
|
|
1500
|
+
return (
|
|
1501
|
+
// For Sigilyph
|
|
1502
|
+
moves.has("defog") || // For Butterfree
|
|
1503
|
+
moves.has("hurricane") && abilities.has("Compound Eyes") || counter.get("Status") > 2 && !counter.setupType
|
|
1504
|
+
);
|
|
1462
1505
|
case "Torrent":
|
|
1463
1506
|
return moves.has("focusenergy") || moves.has("hypervoice");
|
|
1464
1507
|
case "Tough Claws":
|
|
@@ -1564,6 +1607,7 @@ var RandomGen8Teams = class {
|
|
|
1564
1607
|
return "Heavy-Duty Boots";
|
|
1565
1608
|
}
|
|
1566
1609
|
}
|
|
1610
|
+
/** Item generation specific to Random Doubles */
|
|
1567
1611
|
getDoublesItem(ability, types, moves, abilities, counter, teamDetails, species) {
|
|
1568
1612
|
const defensiveStatTotal = species.baseStats.hp + species.baseStats.def + species.baseStats.spd;
|
|
1569
1613
|
if (["dragonenergy", "eruption", "waterspout"].some((m) => moves.has(m)) && counter.damagingMoves.size >= 4)
|
|
@@ -1628,7 +1672,10 @@ var RandomGen8Teams = class {
|
|
|
1628
1672
|
const defensiveStatTotal = species.baseStats.hp + species.baseStats.def + species.baseStats.spd;
|
|
1629
1673
|
if (isLead && !isDoubles && !["Disguise", "Sturdy"].includes(ability) && !moves.has("substitute") && !counter.get("drain") && !counter.get("recoil") && !counter.get("recovery") && (defensiveStatTotal <= 250 && counter.get("hazards") || defensiveStatTotal <= 210))
|
|
1630
1674
|
return "Focus Sash";
|
|
1631
|
-
if (moves.has("clangoroussoul") || moves
|
|
1675
|
+
if (moves.has("clangoroussoul") || // We manually check for speed-boosting moves, rather than using `counter.get('speedsetup')`,
|
|
1676
|
+
// because we want to check for ANY speed boosting move.
|
|
1677
|
+
// In particular, Shift Gear + Boomburst Toxtricity should get Throat Spray.
|
|
1678
|
+
moves.has("boomburst") && Array.from(moves).some((m) => {
|
|
1632
1679
|
var _a;
|
|
1633
1680
|
return (_a = this.dex.moves.get(m).boosts) == null ? void 0 : _a.spe;
|
|
1634
1681
|
}))
|
|
@@ -1639,7 +1686,8 @@ var RandomGen8Teams = class {
|
|
|
1639
1686
|
return "Heavy-Duty Boots";
|
|
1640
1687
|
if (!isDoubles && this.dex.getEffectiveness("Ground", species) >= 2 && !types.has("Poison") && ability !== "Levitate" && !abilities.has("Iron Barbs"))
|
|
1641
1688
|
return "Air Balloon";
|
|
1642
|
-
if (!isDoubles && counter.damagingMoves.size >= 3 && !counter.get("damage") && ability !== "Sturdy" && (species.baseStats.spe >= 90 || !moves.has("voltswitch")) && ["foulplay", "rapidspin", "substitute", "uturn"].every((m) => !moves.has(m)) && (counter.get("speedsetup") ||
|
|
1689
|
+
if (!isDoubles && counter.damagingMoves.size >= 3 && !counter.get("damage") && ability !== "Sturdy" && (species.baseStats.spe >= 90 || !moves.has("voltswitch")) && ["foulplay", "rapidspin", "substitute", "uturn"].every((m) => !moves.has(m)) && (counter.get("speedsetup") || // No Dynamax Buzzwole doesn't want Life Orb with Bulk Up + 3 attacks
|
|
1690
|
+
counter.get("drain") && (!isNoDynamax || species.id !== "buzzwole" || moves.has("roost")) || moves.has("trickroom") || moves.has("psystrike") || species.baseStats.spe > 40 && defensiveStatTotal < 275))
|
|
1643
1691
|
return "Life Orb";
|
|
1644
1692
|
if (!isDoubles && counter.damagingMoves.size >= 4 && !counter.get("Dragon") && !counter.get("Normal")) {
|
|
1645
1693
|
return "Expert Belt";
|
|
@@ -1669,6 +1717,7 @@ var RandomGen8Teams = class {
|
|
|
1669
1717
|
NFE: 88
|
|
1670
1718
|
};
|
|
1671
1719
|
const customScale = {
|
|
1720
|
+
// These Pokemon are too strong and need a lower level
|
|
1672
1721
|
zaciancrowned: 65,
|
|
1673
1722
|
calyrexshadow: 68,
|
|
1674
1723
|
xerneas: 70,
|
|
@@ -1691,6 +1740,7 @@ var RandomGen8Teams = class {
|
|
|
1691
1740
|
polteageist: 84,
|
|
1692
1741
|
wobbuffet: 86,
|
|
1693
1742
|
scrafty: 86,
|
|
1743
|
+
// These Pokemon are too weak and need a higher level
|
|
1694
1744
|
delibird: 100,
|
|
1695
1745
|
vespiquen: 96,
|
|
1696
1746
|
pikachu: 92,
|
|
@@ -1822,7 +1872,13 @@ var RandomGen8Teams = class {
|
|
|
1822
1872
|
const moveIsRejectable = !(species.id === "genesectdouse" && move.id === "technoblast") && !(species.id === "togekiss" && move.id === "nastyplot") && !(species.id === "shuckle" && ["stealthrock", "stickyweb"].includes(move.id)) && (move.category === "Status" || !types.has(move.type) && move.id !== "judgment" || isLowBP && !move.multihit && !abilities.has("Technician"));
|
|
1823
1873
|
const notImportantSetup = !counter.setupType || counter.setupType === "Mixed" || counter.get(counter.setupType) + counter.get("Status") > 3 && !counter.get("hazards") || move.category !== counter.setupType && move.category !== "Status";
|
|
1824
1874
|
if (moveIsRejectable && (!cull && !isSetup && !move.weather && !move.stallingMove && notImportantSetup && !move.damage && (isDoubles ? this.unrejectableMovesInDoubles(move) : this.unrejectableMovesInSingles(move)))) {
|
|
1825
|
-
if (
|
|
1875
|
+
if (
|
|
1876
|
+
// Pokemon should have at least one STAB move
|
|
1877
|
+
!counter.get("stab") && counter.get("physicalpool") + counter.get("specialpool") > 0 && move.id !== "stickyweb" || // Swords Dance Mew should have Brave Bird
|
|
1878
|
+
moves.has("swordsdance") && species.id === "mew" && runEnforcementChecker("Flying") || // Dhelmise should have Anchor Shot
|
|
1879
|
+
abilities.has("Steelworker") && runEnforcementChecker("Steel") || // Check for miscellaneous important moves
|
|
1880
|
+
!isDoubles && runEnforcementChecker("recovery") && move.id !== "stickyweb" || runEnforcementChecker("screens") || runEnforcementChecker("misc") || (isLead || species.id === "shuckle") && runEnforcementChecker("lead") || moves.has("leechseed") && runEnforcementChecker("leechseed")
|
|
1881
|
+
) {
|
|
1826
1882
|
cull = true;
|
|
1827
1883
|
} else if (move.id !== "stickyweb" && !(species.id === "azumarill" && move.id === "aquajet")) {
|
|
1828
1884
|
for (const type of types) {
|
|
@@ -1914,7 +1970,8 @@ var RandomGen8Teams = class {
|
|
|
1914
1970
|
} while (rejectAbility);
|
|
1915
1971
|
if (forme === "Copperajah" && gmax) {
|
|
1916
1972
|
ability = "Heavy Metal";
|
|
1917
|
-
} else if (abilities.has("Guts") &&
|
|
1973
|
+
} else if (abilities.has("Guts") && // for Ursaring in BDSP
|
|
1974
|
+
!abilities.has("Quick Feet") && (species.id === "gurdurr" || species.id === "throh" || moves.has("facade") || moves.has("rest") && moves.has("sleeptalk"))) {
|
|
1918
1975
|
ability = "Guts";
|
|
1919
1976
|
} else if (abilities.has("Moxie") && (counter.get("Physical") > 3 || moves.has("bounce")) && !isDoubles) {
|
|
1920
1977
|
ability = "Moxie";
|
|
@@ -3000,7 +3057,8 @@ var RandomGen7Teams = class extends RandomGen8Teams {
|
|
|
3000
3057
|
case "facade":
|
|
3001
3058
|
return { cull: moves.has("bulkup") || hasRestTalk };
|
|
3002
3059
|
case "hiddenpower":
|
|
3003
|
-
return { cull: moves.has("rest") || !counter.get("stab") && counter.damagingMoves.size < 2 ||
|
|
3060
|
+
return { cull: moves.has("rest") || !counter.get("stab") && counter.damagingMoves.size < 2 || // Force Moonblast on Special-setup Fairies
|
|
3061
|
+
counter.setupType === "Special" && types.has("Fairy") && movePool.includes("moonblast") };
|
|
3004
3062
|
case "hypervoice":
|
|
3005
3063
|
return { cull: moves.has("blizzard") };
|
|
3006
3064
|
case "judgment":
|
|
@@ -3544,7 +3602,8 @@ var RandomGen7Teams = class extends RandomGen8Teams {
|
|
|
3544
3602
|
if (counter.setupType === "Special" && moveid === "hiddenpower" && species.types.length > 1 && counter.get("Special") <= 2 && !types.has(move.type) && !counter.get("Physical") && counter.get("specialpool")) {
|
|
3545
3603
|
cull = true;
|
|
3546
3604
|
}
|
|
3547
|
-
const singlesEnforcement = !["judgment", "lightscreen", "quiverdance", "reflect", "sleeptalk", "toxic"].includes(moveid) && (move.category !== "Status" ||
|
|
3605
|
+
const singlesEnforcement = !["judgment", "lightscreen", "quiverdance", "reflect", "sleeptalk", "toxic"].includes(moveid) && (move.category !== "Status" || // should allow Meganium to cull a recovery move for the sake of STAB
|
|
3606
|
+
!(move.flags.heal && species.id !== "meganium"));
|
|
3548
3607
|
if (!cull && !move.damage && !isSetup && !move.weather && !move.stallingMove && (isDoubles || singlesEnforcement) && (!counter.setupType || counter.setupType === "Mixed" || move.category !== counter.setupType && move.category !== "Status" || counter.get(counter.setupType) + counter.get("Status") > 3 && !counter.get("hazards")) && (move.category === "Status" || !types.has(move.type) || move.basePower && move.basePower < 40 && !move.multihit)) {
|
|
3549
3608
|
if (!counter.get("stab") && !moves.has("nightshade") && !moves.has("seismictoss") && (species.types.length > 1 || species.types[0] !== "Normal" && species.types[0] !== "Psychic" || !moves.has("icebeam") || species.baseStats.spa >= species.baseStats.spd) || moves.has("suckerpunch") && !abilities.has("Contrary") && counter.get("stab") < species.types.length && species.id !== "honchkrow" || ["recover", "roost", "slackoff", "softboiled"].some((m) => movePool.includes(m)) && counter.get("Status") && !counter.setupType && ["healingwish", "switcheroo", "trick", "trickroom"].every((m) => !moves.has(m)) || (movePool.includes("milkdrink") || movePool.includes("shoreup") || movePool.includes("moonlight") && types.size < 2 || movePool.includes("stickyweb") && !counter.setupType && !teamDetails.stickyWeb || movePool.includes("quiverdance") && ["defog", "uturn", "stickyweb"].every((m) => !moves.has(m)) && counter.get("Special") < 4) || isLead && movePool.includes("stealthrock") && counter.get("Status") && !counter.setupType && !counter.get("speedsetup") && !moves.has("substitute") || species.requiredMove && movePool.includes(_sim.toID.call(void 0, species.requiredMove)) || !counter.get("Normal") && (abilities.has("Aerilate") || abilities.has("Pixilate") || abilities.has("Refrigerate") && !moves.has("blizzard"))) {
|
|
3550
3609
|
cull = true;
|
|
@@ -3701,12 +3760,14 @@ var RandomGen7Teams = class extends RandomGen8Teams {
|
|
|
3701
3760
|
} else if (!isDoubles) {
|
|
3702
3761
|
const levelScale = { uber: 76, ou: 80, uu: 82, ru: 84, nu: 86, pu: 88 };
|
|
3703
3762
|
const customScale = {
|
|
3763
|
+
// Banned Ability
|
|
3704
3764
|
Dugtrio: 82,
|
|
3705
3765
|
Gothitelle: 82,
|
|
3706
3766
|
Pelipper: 84,
|
|
3707
3767
|
Politoed: 84,
|
|
3708
3768
|
Torkoal: 84,
|
|
3709
3769
|
Wobbuffet: 82,
|
|
3770
|
+
// Holistic judgement
|
|
3710
3771
|
"Castform-Rainy": 100,
|
|
3711
3772
|
"Castform-Snowy": 100,
|
|
3712
3773
|
"Castform-Sunny": 100,
|
|
@@ -4737,7 +4798,8 @@ var RandomGen6Teams = class extends RandomGen7Teams {
|
|
|
4737
4798
|
case "extremespeed":
|
|
4738
4799
|
return { cull: counter.setupType !== "Physical" && moves.has("vacuumwave") };
|
|
4739
4800
|
case "hiddenpower":
|
|
4740
|
-
return { cull: moves.has("rest") || !counter.get("stab") && counter.damagingMoves.size < 2 ||
|
|
4801
|
+
return { cull: moves.has("rest") || !counter.get("stab") && counter.damagingMoves.size < 2 || // Force Moonblast on Special-setup Fairies
|
|
4802
|
+
counter.setupType === "Special" && types.has("Fairy") && movePool.includes("moonblast") };
|
|
4741
4803
|
case "hypervoice":
|
|
4742
4804
|
return { cull: moves.has("blizzard") || moves.has("return") };
|
|
4743
4805
|
case "judgment":
|
|
@@ -5289,11 +5351,13 @@ var RandomGen6Teams = class extends RandomGen7Teams {
|
|
|
5289
5351
|
pu: 88
|
|
5290
5352
|
};
|
|
5291
5353
|
const customScale = {
|
|
5354
|
+
// Banned Ability
|
|
5292
5355
|
Dugtrio: 82,
|
|
5293
5356
|
Gothitelle: 82,
|
|
5294
5357
|
Ninetales: 84,
|
|
5295
5358
|
Politoed: 84,
|
|
5296
5359
|
Wobbuffet: 82,
|
|
5360
|
+
// Holistic judgement
|
|
5297
5361
|
"Castform-Rainy": 100,
|
|
5298
5362
|
"Castform-Snowy": 100,
|
|
5299
5363
|
"Castform-Sunny": 100,
|
|
@@ -5786,7 +5850,8 @@ var RandomGen5Teams = class extends RandomGen6Teams {
|
|
|
5786
5850
|
const gliscorCase = species.id === "gliscor" && moves.has("protect");
|
|
5787
5851
|
return { cull: ["leechseed", "rest", "wish"].some((m) => moves.has(m)) || gliscorCase };
|
|
5788
5852
|
case "substitute":
|
|
5789
|
-
return { cull: moves.has("doubleedge") && !abilities.has("rockhead") || ["pursuit", "rest", "superpower", "uturn", "voltswitch"].some((m) => moves.has(m)) ||
|
|
5853
|
+
return { cull: moves.has("doubleedge") && !abilities.has("rockhead") || ["pursuit", "rest", "superpower", "uturn", "voltswitch"].some((m) => moves.has(m)) || // Sceptile wants Swords Dance
|
|
5854
|
+
moves.has("acrobatics") && moves.has("earthquake") };
|
|
5790
5855
|
case "thunderwave":
|
|
5791
5856
|
return { cull: !!counter.setupType || !!counter.get("speedsetup") || hasRestTalk || moves.has("discharge") || moves.has("trickroom") };
|
|
5792
5857
|
case "willowisp":
|
|
@@ -6837,7 +6902,8 @@ var RandomGen4Teams = class extends RandomGen5Teams {
|
|
|
6837
6902
|
}
|
|
6838
6903
|
}
|
|
6839
6904
|
if (counter.setupType && !isSetup && move.category !== counter.setupType && counter.get(counter.setupType) < 2 && !moves.has("batonpass")) {
|
|
6840
|
-
if (moveid !== "rest" && moveid !== "sleeptalk" && !(recoveryMoves.includes(moveid) && (moves.has("healbell") || moves.has("refresh"))) && !((moveid === "healbell" || moveid === "refresh") && Array.from(moves).some((id) => recoveryMoves.includes(id))) &&
|
|
6905
|
+
if (moveid !== "rest" && moveid !== "sleeptalk" && !(recoveryMoves.includes(moveid) && (moves.has("healbell") || moves.has("refresh"))) && !((moveid === "healbell" || moveid === "refresh") && Array.from(moves).some((id) => recoveryMoves.includes(id))) && // Reject Status moves only if there is nothing else to reject
|
|
6906
|
+
(move.category !== "Status" || counter.get(counter.setupType) + counter.get("Status") > 3 && counter.get("physicalsetup") + counter.get("specialsetup") < 2)) {
|
|
6841
6907
|
cull = true;
|
|
6842
6908
|
}
|
|
6843
6909
|
}
|
|
@@ -6860,9 +6926,14 @@ var RandomGen4Teams = class extends RandomGen5Teams {
|
|
|
6860
6926
|
teamDetails
|
|
6861
6927
|
);
|
|
6862
6928
|
};
|
|
6863
|
-
const moveIsRejectable = !move.weather && !move.damage && (move.category !== "Status" || !move.flags.heal) && (move.category === "Status" || !types.has(move.type) || move.basePower && move.basePower < 40 && !move.multihit) &&
|
|
6929
|
+
const moveIsRejectable = !move.weather && !move.damage && (move.category !== "Status" || !move.flags.heal) && (move.category === "Status" || !types.has(move.type) || move.basePower && move.basePower < 40 && !move.multihit) && // These moves cannot be rejected in favor of a forced move
|
|
6930
|
+
!["judgment", "lightscreen", "reflect", "sleeptalk"].includes(moveid) && // Setup-supported moves should only be rejected under specific circumstances
|
|
6931
|
+
(counter.get("physicalsetup") + counter.get("specialsetup") < 2 && (!counter.setupType || counter.setupType === "Mixed" || move.category !== counter.setupType && move.category !== "Status" || counter.get(counter.setupType) + counter.get("Status") > 3));
|
|
6864
6932
|
if (!cull && !isSetup && moveIsRejectable) {
|
|
6865
|
-
const canRollForcedMoves =
|
|
6933
|
+
const canRollForcedMoves = (
|
|
6934
|
+
// These moves should always be rolled
|
|
6935
|
+
movePool.includes("spore") || !Array.from(moves).some((id) => recoveryMoves.includes(id)) && (movePool.includes("softboiled") && !moves.has("explosion") || species.baseSpecies === "Arceus" && movePool.includes("recover"))
|
|
6936
|
+
);
|
|
6866
6937
|
const requiresStab = !counter.get("stab") && !counter.get("damage") && (species.types.length > 1 || species.types[0] !== "Normal" && species.types[0] !== "Psychic" || !moves.has("icebeam") || species.baseStats.spa >= species.baseStats.spd);
|
|
6867
6938
|
if (canRollForcedMoves || requiresStab || species.requiredMove && movePool.includes(_sim.toID.call(void 0, species.requiredMove)) || counter.get("defensesetup") && !counter.get("recovery") && !moves.has("rest")) {
|
|
6868
6939
|
cull = true;
|
|
@@ -7044,6 +7115,7 @@ var RandomGen3Teams = class extends RandomGen4Teams {
|
|
|
7044
7115
|
Psychic: (movePool, moves, abilities, types, counter, species) => types.has("Psychic") && (movePool.includes("psychic") || movePool.includes("psychoboost")) && species.baseStats.spa >= 100,
|
|
7045
7116
|
Rock: (movePool, moves, abilities, types, counter, species) => !counter.get("Rock") && species.baseStats.atk >= 100,
|
|
7046
7117
|
Water: (movePool, moves, abilities, types, counter, species) => !counter.get("Water") && counter.setupType !== "Physical" && species.baseStats.spa >= 60,
|
|
7118
|
+
// If the Pokémon has this move, the other move will be forced
|
|
7047
7119
|
protect: (movePool) => movePool.includes("wish"),
|
|
7048
7120
|
sunnyday: (movePool) => movePool.includes("solarbeam"),
|
|
7049
7121
|
sleeptalk: (movePool) => movePool.includes("rest")
|
|
@@ -7097,7 +7169,8 @@ var RandomGen3Teams = class extends RandomGen4Teams {
|
|
|
7097
7169
|
case "sunnyday":
|
|
7098
7170
|
return { cull: counter.damagingMoves.size < 2 || moves.has("rest") };
|
|
7099
7171
|
case "focuspunch":
|
|
7100
|
-
return { cull: counter.damagingMoves.size < 2 || moves.has("rest") || counter.setupType && !moves.has("spore") || !moves.has("substitute") && (counter.get("Physical") < 4 || moves.has("fakeout")) ||
|
|
7172
|
+
return { cull: counter.damagingMoves.size < 2 || moves.has("rest") || counter.setupType && !moves.has("spore") || !moves.has("substitute") && (counter.get("Physical") < 4 || moves.has("fakeout")) || // Breloom likes to have coverage
|
|
7173
|
+
species.id === "breloom" && (moves.has("machpunch") || moves.has("skyuppercut")) };
|
|
7101
7174
|
case "moonlight":
|
|
7102
7175
|
return { cull: moves.has("wish") || moves.has("protect") };
|
|
7103
7176
|
case "perishsong":
|
|
@@ -7201,7 +7274,8 @@ var RandomGen3Teams = class extends RandomGen4Teams {
|
|
|
7201
7274
|
case "gigadrain":
|
|
7202
7275
|
return { cull: moves.has("morningsun") || moves.has("toxic") };
|
|
7203
7276
|
case "hiddenpower":
|
|
7204
|
-
const stabCondition = types.has(move.type) && counter.get(move.type) > 1 && (moves.has("substitute") && !counter.setupType && !moves.has("toxic") ||
|
|
7277
|
+
const stabCondition = types.has(move.type) && counter.get(move.type) > 1 && (moves.has("substitute") && !counter.setupType && !moves.has("toxic") || // This otherwise causes STABless meganium
|
|
7278
|
+
species.id !== "meganium" && moves.has("toxic") && !moves.has("substitute") || restTalk);
|
|
7205
7279
|
return { cull: stabCondition || move.type === "Grass" && moves.has("sunnyday") && moves.has("solarbeam") };
|
|
7206
7280
|
case "brickbreak":
|
|
7207
7281
|
case "crosschop":
|
|
@@ -7332,8 +7406,12 @@ var RandomGen3Teams = class extends RandomGen4Teams {
|
|
|
7332
7406
|
if (counter.setupType === "Physical" && move.category === "Special" && !types.has(move.type) && move.type !== "Fire" || counter.setupType === "Special" && move.category === "Physical" && moveid !== "superpower") {
|
|
7333
7407
|
cull = true;
|
|
7334
7408
|
}
|
|
7335
|
-
const moveIsRejectable = !move.weather && (move.category !== "Status" || !move.flags.heal) && (counter.setupType || !move.stallingMove) &&
|
|
7336
|
-
|
|
7409
|
+
const moveIsRejectable = !move.weather && (move.category !== "Status" || !move.flags.heal) && (counter.setupType || !move.stallingMove) && // These moves cannot be rejected in favor of a forced move
|
|
7410
|
+
!["batonpass", "sleeptalk", "solarbeam", "substitute", "sunnyday"].includes(moveid) && (move.category === "Status" || !types.has(move.type) || move.basePower && move.basePower < 40 && !move.multihit);
|
|
7411
|
+
const requiresStab = !counter.get("stab") && !moves.has("seismictoss") && !moves.has("nightshade") && species.id !== "castform" && species.id !== "umbreon" && // If a Flying-type has Psychic, it doesn't need STAB
|
|
7412
|
+
!(moves.has("psychic") && types.has("Flying")) && !(types.has("Ghost") && species.baseStats.spa > species.baseStats.atk) && !// With Calm Mind, Lugia and pure Normal-types are fine without STAB
|
|
7413
|
+
(counter.setupType === "Special" && (species.id === "lugia" || types.has("Normal") && species.types.length < 2)) && !// With Swords Dance, Dark-types and pure Water-types are fine without STAB
|
|
7414
|
+
(counter.setupType === "Physical" && (types.has("Water") && species.types.length < 2 || types.has("Dark"))) && counter.get("physicalpool") + counter.get("specialpool") > 0;
|
|
7337
7415
|
const runEnforcementChecker = (checkerName) => {
|
|
7338
7416
|
if (!this.moveEnforcementCheckers[checkerName])
|
|
7339
7417
|
return false;
|
|
@@ -7732,9 +7810,15 @@ var RandomGen2Teams = class extends RandomGen3Teams {
|
|
|
7732
7810
|
if (counter.setupType === "Physical" && move.category === "Special" && !counter.get("Physical")) {
|
|
7733
7811
|
cull = true;
|
|
7734
7812
|
}
|
|
7735
|
-
const moveIsRejectable = (move.category !== "Status" || !move.flags.heal) &&
|
|
7813
|
+
const moveIsRejectable = (move.category !== "Status" || !move.flags.heal) && // These moves cannot be rejected in favor of a forced move
|
|
7814
|
+
!["batonpass", "sleeptalk", "spikes", "sunnyday"].includes(move.id) && (move.category === "Status" || !types.has(move.type) || move.basePower && move.basePower < 40);
|
|
7736
7815
|
if (!cull && !isSetup && moveIsRejectable && (counter.setupType || !move.stallingMove)) {
|
|
7737
|
-
if (
|
|
7816
|
+
if (
|
|
7817
|
+
// Pokemon should usually have at least one STAB move
|
|
7818
|
+
!counter.get("stab") && !counter.get("damage") && !types.has("Ghost") && counter.get("physicalpool") + counter.get("specialpool") > 0 || (movePool.includes("megahorn") || movePool.includes("softboiled") && moves.has("present")) || // Rest + Sleep Talk should be selected together
|
|
7819
|
+
(moves.has("rest") && movePool.includes("sleeptalk") || moves.has("sleeptalk") && movePool.includes("rest")) || // Sunny Day + Solar Beam should be selected together
|
|
7820
|
+
(moves.has("sunnyday") && movePool.includes("solarbeam") || moves.has("solarbeam") && movePool.includes("sunnyday")) || ["milkdrink", "recover", "spore"].some((m) => movePool.includes(m))
|
|
7821
|
+
) {
|
|
7738
7822
|
cull = true;
|
|
7739
7823
|
} else {
|
|
7740
7824
|
for (const type of types) {
|
|
@@ -7812,6 +7896,7 @@ var RandomGen2Teams = class extends RandomGen3Teams {
|
|
|
7812
7896
|
ivs,
|
|
7813
7897
|
item: this.getItem("None", types, moves, counter, species),
|
|
7814
7898
|
level,
|
|
7899
|
+
// No shiny chance because Gen 2 shinies have bad IVs
|
|
7815
7900
|
shiny: false,
|
|
7816
7901
|
gender: species.gender ? species.gender : "M"
|
|
7817
7902
|
};
|
|
@@ -7820,6 +7905,7 @@ var RandomGen2Teams = class extends RandomGen3Teams {
|
|
|
7820
7905
|
|
|
7821
7906
|
// src/gen1.ts
|
|
7822
7907
|
var RandomGen1Teams = class extends RandomGen2Teams {
|
|
7908
|
+
// Challenge Cup or CC teams are basically fully random teams.
|
|
7823
7909
|
randomCCTeam() {
|
|
7824
7910
|
this.enforceNoDirectCustomBanlistChanges();
|
|
7825
7911
|
const team = [];
|
|
@@ -7894,6 +7980,7 @@ var RandomGen1Teams = class extends RandomGen2Teams {
|
|
|
7894
7980
|
}
|
|
7895
7981
|
return team;
|
|
7896
7982
|
}
|
|
7983
|
+
// Random team generation for Gen 1 Random Battles.
|
|
7897
7984
|
randomTeam() {
|
|
7898
7985
|
this.enforceNoDirectCustomBanlistChanges();
|
|
7899
7986
|
const seed = this.prng.seed;
|
|
@@ -8012,6 +8099,9 @@ var RandomGen1Teams = class extends RandomGen2Teams {
|
|
|
8012
8099
|
}
|
|
8013
8100
|
return { cull: false };
|
|
8014
8101
|
}
|
|
8102
|
+
/**
|
|
8103
|
+
* Random set generation for Gen 1 Random Battles.
|
|
8104
|
+
*/
|
|
8015
8105
|
randomSet(species) {
|
|
8016
8106
|
species = this.dex.species.get(species);
|
|
8017
8107
|
if (!species.exists)
|
|
@@ -8208,6 +8298,7 @@ var RandomGen1Teams = class extends RandomGen2Teams {
|
|
|
8208
8298
|
nature: "",
|
|
8209
8299
|
level,
|
|
8210
8300
|
shiny: false,
|
|
8301
|
+
// Hacky but the only way to communicate stats/level generation properly
|
|
8211
8302
|
hc: hackmonsCup[species.id]
|
|
8212
8303
|
});
|
|
8213
8304
|
}
|
|
@@ -8386,6 +8477,7 @@ function sereneGraceBenefits2(move) {
|
|
|
8386
8477
|
}
|
|
8387
8478
|
var RandomTeams = class {
|
|
8388
8479
|
constructor(dex, format, prng) {
|
|
8480
|
+
// TODO: Make types for this
|
|
8389
8481
|
this.randomSets = randomSetsJSON;
|
|
8390
8482
|
this.randomDoublesSets = randomSetsJSON;
|
|
8391
8483
|
this.dex = dex;
|
|
@@ -8465,6 +8557,10 @@ var RandomTeams = class {
|
|
|
8465
8557
|
random(m, n) {
|
|
8466
8558
|
return this.prng.next(m, n);
|
|
8467
8559
|
}
|
|
8560
|
+
/**
|
|
8561
|
+
* Remove an element from an unsorted array significantly faster
|
|
8562
|
+
* than .splice
|
|
8563
|
+
*/
|
|
8468
8564
|
fastPop(list, index) {
|
|
8469
8565
|
const length = list.length;
|
|
8470
8566
|
if (index < 0 || index >= list.length) {
|
|
@@ -8475,6 +8571,10 @@ var RandomTeams = class {
|
|
|
8475
8571
|
list.pop();
|
|
8476
8572
|
return element;
|
|
8477
8573
|
}
|
|
8574
|
+
/**
|
|
8575
|
+
* Remove a random element from an unsorted array and return it.
|
|
8576
|
+
* Uses the battle's RNG if in a battle.
|
|
8577
|
+
*/
|
|
8478
8578
|
sampleNoReplace(list) {
|
|
8479
8579
|
const length = list.length;
|
|
8480
8580
|
if (length === 0)
|
|
@@ -8482,6 +8582,11 @@ var RandomTeams = class {
|
|
|
8482
8582
|
const index = this.random(length);
|
|
8483
8583
|
return this.fastPop(list, index);
|
|
8484
8584
|
}
|
|
8585
|
+
/**
|
|
8586
|
+
* Removes n random elements from an unsorted array and returns them.
|
|
8587
|
+
* If n is less than the array's length, randomly removes and returns all the elements
|
|
8588
|
+
* in the array (so the returned array could have length < n).
|
|
8589
|
+
*/
|
|
8485
8590
|
multipleSamplesNoReplace(list, n) {
|
|
8486
8591
|
const samples = [];
|
|
8487
8592
|
while (samples.length < n && list.length) {
|
|
@@ -8489,7 +8594,13 @@ var RandomTeams = class {
|
|
|
8489
8594
|
}
|
|
8490
8595
|
return samples;
|
|
8491
8596
|
}
|
|
8597
|
+
/**
|
|
8598
|
+
* Check if user has directly tried to ban/unban/restrict things in a custom battle.
|
|
8599
|
+
* Doesn't count bans nested inside other formats/rules.
|
|
8600
|
+
*/
|
|
8492
8601
|
hasDirectCustomBanlistChanges() {
|
|
8602
|
+
if (this.format.banlist.length || this.format.restricted.length || this.format.unbanlist.length)
|
|
8603
|
+
return true;
|
|
8493
8604
|
if (!this.format.customRules)
|
|
8494
8605
|
return false;
|
|
8495
8606
|
for (const rule of this.format.customRules) {
|
|
@@ -8500,11 +8611,17 @@ var RandomTeams = class {
|
|
|
8500
8611
|
}
|
|
8501
8612
|
return false;
|
|
8502
8613
|
}
|
|
8614
|
+
/**
|
|
8615
|
+
* Inform user when custom bans are unsupported in a team generator.
|
|
8616
|
+
*/
|
|
8503
8617
|
enforceNoDirectCustomBanlistChanges() {
|
|
8504
8618
|
if (this.hasDirectCustomBanlistChanges()) {
|
|
8505
8619
|
throw new Error(`Custom bans are not currently supported in ${this.format.name}.`);
|
|
8506
8620
|
}
|
|
8507
8621
|
}
|
|
8622
|
+
/**
|
|
8623
|
+
* Inform user when complex bans are unsupported in a team generator.
|
|
8624
|
+
*/
|
|
8508
8625
|
enforceNoDirectComplexBans() {
|
|
8509
8626
|
if (!this.format.customRules)
|
|
8510
8627
|
return false;
|
|
@@ -8514,6 +8631,9 @@ var RandomTeams = class {
|
|
|
8514
8631
|
}
|
|
8515
8632
|
}
|
|
8516
8633
|
}
|
|
8634
|
+
/**
|
|
8635
|
+
* Validate set element pool size is sufficient to support size requirements after simple bans.
|
|
8636
|
+
*/
|
|
8517
8637
|
enforceCustomPoolSizeNoComplexBans(effectTypeName, basicEffectPool, requiredCount, requiredCountExplanation) {
|
|
8518
8638
|
if (basicEffectPool.length >= requiredCount)
|
|
8519
8639
|
return;
|
|
@@ -8676,6 +8796,7 @@ var RandomTeams = class {
|
|
|
8676
8796
|
this.incompatibleMoves(moves, movePool, "bodypress", "mirrorcoat");
|
|
8677
8797
|
this.incompatibleMoves(moves, movePool, "toxic", "clearsmog");
|
|
8678
8798
|
}
|
|
8799
|
+
// Checks for and removes incompatible moves, starting with the first move in movesA.
|
|
8679
8800
|
incompatibleMoves(moves, movePool, movesA, movesB) {
|
|
8680
8801
|
const moveArrayA = Array.isArray(movesA) ? movesA : [movesA];
|
|
8681
8802
|
const moveArrayB = Array.isArray(movesB) ? movesB : [movesB];
|
|
@@ -8702,6 +8823,7 @@ var RandomTeams = class {
|
|
|
8702
8823
|
}
|
|
8703
8824
|
}
|
|
8704
8825
|
}
|
|
8826
|
+
// Adds a move to the moveset, returns the MoveCounter
|
|
8705
8827
|
addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, movePool, teraType, role) {
|
|
8706
8828
|
moves.add(move);
|
|
8707
8829
|
this.fastPop(movePool, movePool.indexOf(move));
|
|
@@ -8709,6 +8831,7 @@ var RandomTeams = class {
|
|
|
8709
8831
|
this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, teraType, role);
|
|
8710
8832
|
return counter;
|
|
8711
8833
|
}
|
|
8834
|
+
// Returns the type of a given move for STAB/coverage enforcement purposes
|
|
8712
8835
|
getMoveType(move, species, abilities, teraType) {
|
|
8713
8836
|
if (move.id === "terablast")
|
|
8714
8837
|
return teraType;
|
|
@@ -8735,6 +8858,7 @@ var RandomTeams = class {
|
|
|
8735
8858
|
}
|
|
8736
8859
|
return moveType;
|
|
8737
8860
|
}
|
|
8861
|
+
// Generate random moveset for a given species, role, tera type.
|
|
8738
8862
|
randomMoveset(types, abilities, teamDetails, species, isLead, isDoubles, movePool, teraType, role) {
|
|
8739
8863
|
const moves = /* @__PURE__ */ new Set();
|
|
8740
8864
|
let counter = this.queryMoves(moves, species, teraType, abilities);
|
|
@@ -9398,6 +9522,8 @@ var RandomTeams = class {
|
|
|
9398
9522
|
if (this.dex.getEffectiveness("Rock", species) >= 2)
|
|
9399
9523
|
return "Heavy-Duty Boots";
|
|
9400
9524
|
}
|
|
9525
|
+
/** Item generation specific to Random Doubles */
|
|
9526
|
+
// This will be changed and used later, once doubles is actually coming out.
|
|
9401
9527
|
getDoublesItem(ability, types, moves, counter, teamDetails, species, teraType, role) {
|
|
9402
9528
|
const defensiveStatTotal = species.baseStats.hp + species.baseStats.def + species.baseStats.spd;
|
|
9403
9529
|
if (["dragonenergy", "eruption", "waterspout"].some((m) => moves.has(m)) && counter.damagingMoves.size >= 4)
|
|
@@ -9652,6 +9778,7 @@ var RandomTeams = class {
|
|
|
9652
9778
|
}
|
|
9653
9779
|
return [pokemonPool, baseSpeciesPool];
|
|
9654
9780
|
}
|
|
9781
|
+
// Doubles sets are the same as singles for now
|
|
9655
9782
|
randomTeam() {
|
|
9656
9783
|
this.enforceNoDirectCustomBanlistChanges();
|
|
9657
9784
|
const seed = this.prng.seed;
|