@pkmn/randoms 0.6.4 → 0.7.0

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/gen3.js DELETED
@@ -1,621 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RandomGen3Teams = void 0;
4
- const gen4_1 = require("./gen4");
5
- const utils_1 = require("./utils");
6
- class RandomGen3Teams extends gen4_1.RandomGen4Teams {
7
- constructor(dex, format, prng) {
8
- super(dex, format, prng);
9
- this.battleHasDitto = false;
10
- this.battleHasWobbuffet = false;
11
- this.moveEnforcementCheckers = {
12
- Bug: (movePool, moves, abilities, types, counter, species) => (movePool.includes('megahorn') || (!species.types[1] && movePool.includes('hiddenpowerbug'))),
13
- Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
14
- Fighting: (movePool, moves, abilities, types, counter) => !counter.get('Fighting'),
15
- Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
16
- Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
17
- Normal: (movePool, moves, abilities, types, counter, species) => {
18
- if (species.id === 'blissey' && movePool.includes('softboiled'))
19
- return true;
20
- return !counter.get('Normal') && counter.setupType === 'Physical';
21
- },
22
- Psychic: (movePool, moves, abilities, types, counter, species) => (types.has('Psychic') &&
23
- (movePool.includes('psychic') || movePool.includes('psychoboost')) &&
24
- species.baseStats.spa >= 100),
25
- Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock') && species.baseStats.atk >= 100,
26
- Water: (movePool, moves, abilities, types, counter, species) => (!counter.get('Water') && counter.setupType !== 'Physical' && species.baseStats.spa >= 60),
27
- // If the Pokémon has this move, the other move will be forced
28
- protect: movePool => movePool.includes('wish'),
29
- sunnyday: movePool => movePool.includes('solarbeam'),
30
- sleeptalk: movePool => movePool.includes('rest'),
31
- };
32
- }
33
- shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species) {
34
- const restTalk = moves.has('rest') && moves.has('sleeptalk');
35
- switch (move.id) {
36
- // Set up once and only if we have the moves for it
37
- case 'bulkup':
38
- case 'curse':
39
- case 'dragondance':
40
- case 'swordsdance':
41
- return {
42
- cull: ((counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1) ||
43
- (counter.get('Physical') + counter.get('physicalpool') < 2 && !moves.has('batonpass') && !restTalk)),
44
- isSetup: true,
45
- };
46
- case 'calmmind':
47
- return {
48
- cull: (counter.setupType !== 'Special' ||
49
- (counter.get('Special') + counter.get('specialpool') < 2 && !moves.has('batonpass') && !restTalk)),
50
- isSetup: true,
51
- };
52
- case 'agility':
53
- return {
54
- cull: (counter.damagingMoves.size < 2 && !moves.has('batonpass')) || moves.has('substitute') || restTalk,
55
- isSetup: !counter.setupType,
56
- };
57
- // Not very useful without their supporting moves
58
- case 'amnesia':
59
- case 'sleeptalk':
60
- if (moves.has('roar'))
61
- return { cull: true };
62
- if (!moves.has('rest'))
63
- return { cull: true };
64
- if (movePool.length > 1) {
65
- const rest = movePool.indexOf('rest');
66
- if (rest >= 0)
67
- this.fastPop(movePool, rest);
68
- }
69
- break;
70
- case 'barrier':
71
- return { cull: !moves.has('calmmind') && !moves.has('batonpass') && !moves.has('mirrorcoat') };
72
- case 'batonpass':
73
- return { cull: ((!counter.setupType && !counter.get('speedsetup')) &&
74
- ['meanlook', 'spiderweb', 'substitute', 'wish'].every(m => !moves.has(m))) };
75
- case 'endeavor':
76
- case 'flail':
77
- case 'reversal':
78
- return { cull: restTalk || (!moves.has('endure') && !moves.has('substitute')) };
79
- case 'endure':
80
- return { cull: movePool.includes('destinybond') };
81
- case 'extremespeed':
82
- case 'raindance':
83
- case 'sunnyday':
84
- return { cull: counter.damagingMoves.size < 2 || moves.has('rest') };
85
- case 'focuspunch':
86
- return { cull: ((counter.damagingMoves.size < 2 || moves.has('rest') || counter.setupType && !moves.has('spore')) ||
87
- (!moves.has('substitute') && (counter.get('Physical') < 4 || moves.has('fakeout'))) ||
88
- // Breloom likes to have coverage
89
- (species.id === 'breloom' && (moves.has('machpunch') || moves.has('skyuppercut')))) };
90
- case 'moonlight':
91
- return { cull: moves.has('wish') || moves.has('protect') };
92
- case 'perishsong':
93
- return { cull: !moves.has('meanlook') && !moves.has('spiderweb') };
94
- case 'protect':
95
- return { cull: !abilities.has('Speed Boost') && ['perishsong', 'toxic', 'wish'].every(m => !moves.has(m)) };
96
- case 'refresh':
97
- return { cull: !counter.setupType };
98
- case 'rest':
99
- return { cull: (movePool.includes('sleeptalk') ||
100
- (!moves.has('sleeptalk') && (!!counter.get('recovery') || movePool.includes('curse')))) };
101
- case 'solarbeam':
102
- if (movePool.length > 1) {
103
- const sunnyday = movePool.indexOf('sunnyday');
104
- if (sunnyday >= 0)
105
- this.fastPop(movePool, sunnyday);
106
- }
107
- return { cull: !moves.has('sunnyday') };
108
- // Bad after setup
109
- case 'aromatherapy':
110
- case 'healbell':
111
- return { cull: moves.has('rest') || !!teamDetails.statusCure };
112
- case 'confuseray':
113
- return { cull: !!counter.setupType || restTalk };
114
- case 'counter':
115
- case 'mirrorcoat':
116
- return { cull: !!counter.setupType || ['rest', 'substitute', 'toxic'].some(m => moves.has(m)) };
117
- case 'destinybond':
118
- return { cull: !!counter.setupType || moves.has('explosion') || moves.has('selfdestruct') };
119
- case 'doubleedge':
120
- case 'facade':
121
- case 'fakeout':
122
- case 'waterspout':
123
- return { cull: ((!types.has(move.type) && counter.get('Status') >= 1) ||
124
- (move.id === 'doubleedge' && moves.has('return'))) };
125
- case 'encore':
126
- case 'painsplit':
127
- case 'recover':
128
- case 'yawn':
129
- return { cull: restTalk };
130
- case 'explosion':
131
- case 'machpunch':
132
- case 'selfdestruct':
133
- // Snorlax doesn't want to roll selfdestruct as its only STAB move
134
- const snorlaxCase = species.id === 'snorlax' && !moves.has('return') && !moves.has('bodyslam');
135
- return { cull: snorlaxCase || moves.has('rest') || moves.has('substitute') || !!counter.get('recovery') };
136
- case 'haze':
137
- return { cull: !!counter.setupType || moves.has('raindance') || restTalk };
138
- case 'icywind':
139
- case 'pursuit':
140
- case 'superpower':
141
- case 'transform':
142
- return { cull: !!counter.setupType || moves.has('rest') };
143
- case 'leechseed':
144
- return { cull: !!counter.setupType || moves.has('explosion') };
145
- case 'stunspore':
146
- return { cull: moves.has('sunnyday') || moves.has('toxic') };
147
- case 'lightscreen':
148
- return { cull: !!counter.setupType || !!counter.get('speedsetup') };
149
- case 'meanlook':
150
- case 'spiderweb':
151
- return { cull: !!counter.get('speedsetup') || (!moves.has('batonpass') && !moves.has('perishsong')) };
152
- case 'morningsun':
153
- return { cull: counter.get('speedsetup') >= 1 };
154
- case 'quickattack':
155
- return { cull: (!!counter.get('speedsetup') ||
156
- moves.has('substitute') ||
157
- (!types.has('Normal') && !!counter.get('Status'))) };
158
- case 'rapidspin':
159
- return { cull: !!counter.setupType || moves.has('rest') || !!teamDetails.rapidSpin };
160
- case 'reflect':
161
- return { cull: !!counter.setupType || !!counter.get('speedsetup') };
162
- case 'roar':
163
- return { cull: moves.has('sleeptalk') || moves.has('rest') };
164
- case 'seismictoss':
165
- return { cull: !!counter.setupType || moves.has('thunderbolt') };
166
- case 'spikes':
167
- return { cull: !!counter.setupType || moves.has('substitute') || restTalk || !!teamDetails.spikes };
168
- case 'substitute':
169
- const restOrDD = moves.has('rest') || (moves.has('dragondance') && !moves.has('bellydrum'));
170
- // This cull condition otherwise causes mono-solarbeam Entei
171
- return { cull: restOrDD || (species.id !== 'entei' && !moves.has('batonpass') && movePool.includes('calmmind')) };
172
- case 'thunderwave':
173
- return { cull: !!counter.setupType || moves.has('bodyslam') || moves.has('substitute') || restTalk };
174
- case 'toxic':
175
- return { cull: (!!counter.setupType ||
176
- !!counter.get('speedsetup') ||
177
- ['endure', 'focuspunch', 'raindance', 'yawn', 'hypnosis'].some(m => moves.has(m))) };
178
- case 'trick':
179
- return { cull: counter.get('Status') > 1 };
180
- case 'willowisp':
181
- return { cull: !!counter.setupType || moves.has('hypnosis') || moves.has('toxic') };
182
- // Bit redundant to have both
183
- case 'bodyslam':
184
- return { cull: moves.has('return') && !!counter.get('Status') };
185
- case 'headbutt':
186
- return { cull: !moves.has('bodyslam') && !moves.has('thunderwave') };
187
- case 'return':
188
- return { cull: (moves.has('endure') ||
189
- (moves.has('substitute') && moves.has('flail')) ||
190
- (moves.has('bodyslam') && !counter.get('Status'))) };
191
- case 'fireblast':
192
- return { cull: moves.has('flamethrower') && !!counter.get('Status') };
193
- case 'flamethrower':
194
- return { cull: moves.has('fireblast') && !counter.get('Status') };
195
- case 'overheat':
196
- return { cull: moves.has('flamethrower') || moves.has('substitute') };
197
- case 'hydropump':
198
- return { cull: moves.has('surf') && !!counter.get('Status') };
199
- case 'surf':
200
- return { cull: moves.has('hydropump') && !counter.get('Status') };
201
- case 'gigadrain':
202
- return { cull: moves.has('morningsun') || moves.has('toxic') };
203
- case 'hiddenpower':
204
- const stabCondition = types.has(move.type) && counter.get(move.type) > 1 && ((moves.has('substitute') && !counter.setupType && !moves.has('toxic')) ||
205
- // This otherwise causes STABless meganium
206
- (species.id !== 'meganium' && moves.has('toxic') && !moves.has('substitute')) ||
207
- restTalk);
208
- return { cull: stabCondition || (move.type === 'Grass' && moves.has('sunnyday') && moves.has('solarbeam')) };
209
- case 'brickbreak':
210
- case 'crosschop':
211
- case 'skyuppercut':
212
- return { cull: moves.has('substitute') && (moves.has('focuspunch') || movePool.includes('focuspunch')) };
213
- case 'earthquake':
214
- return { cull: moves.has('bonemerang') };
215
- }
216
- return { cull: false };
217
- }
218
- getItem(ability, types, moves, counter, species) {
219
- // First, the high-priority items
220
- if (species.name === 'Ditto')
221
- return this.sample(['Metal Powder', 'Quick Claw']);
222
- if (species.name === 'Farfetch\u2019d')
223
- return 'Stick';
224
- if (species.name === 'Marowak')
225
- return 'Thick Club';
226
- if (species.name === 'Pikachu')
227
- return 'Light Ball';
228
- if (species.name === 'Shedinja')
229
- return 'Lum Berry';
230
- if (species.name === 'Unown')
231
- return 'Twisted Spoon';
232
- if (moves.has('trick'))
233
- return 'Choice Band';
234
- if (moves.has('rest') && !moves.has('sleeptalk') && !['Early Bird', 'Natural Cure', 'Shed Skin'].includes(ability)) {
235
- return 'Chesto Berry';
236
- }
237
- // Medium priority items
238
- if (moves.has('dragondance') && ability !== 'Natural Cure')
239
- return 'Lum Berry';
240
- if ((moves.has('bellydrum') && counter.get('Physical') - counter.get('priority') > 1) || (((moves.has('swordsdance') && counter.get('Status') < 2) || (moves.has('bulkup') && moves.has('substitute'))) &&
241
- !counter.get('priority') &&
242
- species.baseStats.spe >= 60 && species.baseStats.spe <= 95)) {
243
- return 'Salac Berry';
244
- }
245
- if (moves.has('endure') || (moves.has('substitute') &&
246
- ['bellydrum', 'endeavor', 'flail', 'reversal'].some(m => moves.has(m)))) {
247
- return (species.baseStats.spe <= 100 && ability !== 'Speed Boost' && !counter.get('speedsetup') && !moves.has('focuspunch')) ? 'Salac Berry' : 'Liechi Berry';
248
- }
249
- if (moves.has('substitute') && counter.get('Physical') >= 3 && species.baseStats.spe >= 120)
250
- return 'Liechi Berry';
251
- if ((moves.has('substitute') || moves.has('raindance')) && counter.get('Special') >= 3)
252
- return 'Petaya Berry';
253
- if (counter.get('Physical') >= 4 && !moves.has('fakeout'))
254
- return 'Choice Band';
255
- if (counter.get('Physical') >= 3 && !moves.has('rapidspin') && (['firepunch', 'icebeam', 'overheat'].some(m => moves.has(m)) ||
256
- Array.from(moves).some(m => {
257
- const moveData = this.dex.moves.get(m);
258
- return moveData.category === 'Special' && types.has(moveData.type);
259
- }))) {
260
- return 'Choice Band';
261
- }
262
- // Default to Leftovers
263
- return 'Leftovers';
264
- }
265
- shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species) {
266
- switch (ability) {
267
- case 'Chlorophyll':
268
- return !moves.has('sunnyday') && !teamDetails['sun'];
269
- case 'Compound Eyes':
270
- return !counter.get('inaccurate');
271
- case 'Hustle':
272
- return counter.get('Physical') < 2;
273
- case 'Lightning Rod':
274
- return species.types.includes('Ground');
275
- case 'Overgrow':
276
- return !counter.get('Grass');
277
- case 'Rock Head':
278
- return !counter.get('recoil');
279
- case 'Sand Veil':
280
- return !teamDetails['sand'];
281
- case 'Serene Grace':
282
- return species.id === 'blissey';
283
- case 'Sturdy':
284
- // Sturdy is bad.
285
- return true;
286
- case 'Swift Swim':
287
- return !moves.has('raindance') && !teamDetails['rain'];
288
- case 'Swarm':
289
- return !counter.get('Bug');
290
- case 'Torrent':
291
- return !counter.get('Water');
292
- case 'Water Absorb':
293
- return abilities.has('Swift Swim');
294
- }
295
- return false;
296
- }
297
- randomSet(species, teamDetails = {}) {
298
- species = this.dex.species.get(species);
299
- let forme = species.name;
300
- if (typeof species.battleOnly === 'string')
301
- forme = species.battleOnly;
302
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id))).slice();
303
- const rejectedPool = [];
304
- const moves = new Set();
305
- let ability = '';
306
- const evs = { hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85 };
307
- const ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
308
- let availableHP = 0;
309
- for (const setMoveid of movePool) {
310
- if (setMoveid.startsWith('hiddenpower'))
311
- availableHP++;
312
- }
313
- const types = new Set(species.types);
314
- const abilities = new Set(Object.values(species.abilities));
315
- let counter;
316
- // We use a special variable to track Hidden Power
317
- // so that we can check for all Hidden Powers at once
318
- let hasHiddenPower = false;
319
- do {
320
- // Choose next 4 moves from learnset/viable moves and add them to moves list:
321
- while (moves.size < this.maxMoveCount && movePool.length) {
322
- const moveid = this.sampleNoReplace(movePool);
323
- if (moveid.startsWith('hiddenpower')) {
324
- availableHP--;
325
- if (hasHiddenPower)
326
- continue;
327
- hasHiddenPower = true;
328
- }
329
- moves.add(moveid);
330
- }
331
- while (moves.size < this.maxMoveCount && rejectedPool.length) {
332
- const moveid = this.sampleNoReplace(rejectedPool);
333
- if (moveid.startsWith('hiddenpower')) {
334
- if (hasHiddenPower)
335
- continue;
336
- hasHiddenPower = true;
337
- }
338
- moves.add(moveid);
339
- }
340
- counter = this.queryMoves(moves, species.types, abilities, movePool);
341
- // Iterate through the moves again, this time to cull them:
342
- for (const moveid of moves) {
343
- const move = this.dex.moves.get(moveid);
344
- let { cull, isSetup } = this.shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species);
345
- // This move doesn't satisfy our setup requirements:
346
- if ((counter.setupType === 'Physical' && move.category === 'Special' && !types.has(move.type) && move.type !== 'Fire') ||
347
- (counter.setupType === 'Special' && move.category === 'Physical' && moveid !== 'superpower')) {
348
- cull = true;
349
- }
350
- const moveIsRejectable = (!move.weather &&
351
- (move.category !== 'Status' || !move.flags.heal) &&
352
- (counter.setupType || !move.stallingMove) &&
353
- // These moves cannot be rejected in favor of a forced move
354
- !['batonpass', 'sleeptalk', 'solarbeam', 'substitute', 'sunnyday'].includes(moveid) &&
355
- (move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40 && !move.multihit)));
356
- // Pokemon should usually have at least one STAB move
357
- const requiresStab = (!counter.get('stab') &&
358
- !moves.has('seismictoss') && !moves.has('nightshade') &&
359
- species.id !== 'castform' &&
360
- // If a Flying-type has Psychic, it doesn't need STAB
361
- !(moves.has('psychic') && types.has('Flying')) &&
362
- !(types.has('Ghost') && species.baseStats.spa > species.baseStats.atk) &&
363
- !(
364
- // With Calm Mind, Lugia and pure Normal-types are fine without STAB
365
- counter.setupType === 'Special' && (species.id === 'lugia' ||
366
- (types.has('Normal') && species.types.length < 2))) &&
367
- !(
368
- // With Swords Dance, Dark-types and pure Water-types are fine without STAB
369
- counter.setupType === 'Physical' &&
370
- ((types.has('Water') && species.types.length < 2) || types.has('Dark'))) &&
371
- counter.get('physicalpool') + counter.get('specialpool') > 0);
372
- const runEnforcementChecker = (checkerName) => {
373
- if (!this.moveEnforcementCheckers[checkerName])
374
- return false;
375
- return this.moveEnforcementCheckers[checkerName](movePool, moves, abilities, types, counter, species, teamDetails);
376
- };
377
- if (!cull && !isSetup && moveIsRejectable) {
378
- // There may be more important moves that this Pokemon needs
379
- if (requiresStab ||
380
- (counter.setupType && counter.get(counter.setupType) < 2) ||
381
- (moves.has('substitute') && movePool.includes('morningsun')) ||
382
- ['meteormash', 'spore', 'recover'].some(m => movePool.includes(m))) {
383
- cull = true;
384
- }
385
- else {
386
- // Pokemon should have moves that benefit their typing and their other moves
387
- for (const type of types) {
388
- if (runEnforcementChecker(type)) {
389
- cull = true;
390
- }
391
- }
392
- for (const m of moves) {
393
- if (runEnforcementChecker(m))
394
- cull = true;
395
- }
396
- }
397
- }
398
- // Sleep Talk shouldn't be selected without Rest
399
- if (moveid === 'rest' && cull) {
400
- const sleeptalk = movePool.indexOf('sleeptalk');
401
- if (sleeptalk >= 0) {
402
- if (movePool.length < 2) {
403
- cull = false;
404
- }
405
- else {
406
- this.fastPop(movePool, sleeptalk);
407
- }
408
- }
409
- }
410
- // Remove rejected moves from the move list
411
- const moveIsHP = moveid.startsWith('hiddenpower');
412
- if (cull &&
413
- (movePool.length - availableHP || availableHP && (moveIsHP || !hasHiddenPower))) {
414
- if (move.category !== 'Status' && !move.damage && (!moveIsHP || !availableHP)) {
415
- rejectedPool.push(moveid);
416
- }
417
- if (moveIsHP)
418
- hasHiddenPower = false;
419
- moves.delete(moveid);
420
- break;
421
- }
422
- if (cull && rejectedPool.length) {
423
- if (moveIsHP)
424
- hasHiddenPower = false;
425
- moves.delete(moveid);
426
- break;
427
- }
428
- }
429
- } while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
430
- if (hasHiddenPower) {
431
- let hpType;
432
- for (const move of moves) {
433
- if (move.startsWith('hiddenpower'))
434
- hpType = move.substr(11);
435
- }
436
- if (!hpType)
437
- throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
438
- const HPivs = this.dex.types.get(hpType).HPivs;
439
- let iv;
440
- for (iv in HPivs) {
441
- ivs[iv] = HPivs[iv];
442
- }
443
- }
444
- const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a)).filter(a => a.gen === 3);
445
- utils_1.Utils.sortBy(abilityData, abil => -abil.rating);
446
- let ability0 = abilityData[0];
447
- let ability1 = abilityData[1];
448
- if (abilityData[1]) {
449
- if (ability0.rating <= ability1.rating && this.randomChance(1, 2)) {
450
- [ability0, ability1] = [ability1, ability0];
451
- }
452
- else if (ability0.rating - 0.6 <= ability1.rating && this.randomChance(2, 3)) {
453
- [ability0, ability1] = [ability1, ability0];
454
- }
455
- ability = ability0.name;
456
- while (this.shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species)) {
457
- if (ability === ability0.name && ability1.rating > 1) {
458
- ability = ability1.name;
459
- }
460
- else {
461
- // Default to the highest rated ability if all are rejected
462
- ability = abilityData[0].name;
463
- break;
464
- }
465
- }
466
- }
467
- else {
468
- ability = abilityData[0].name;
469
- }
470
- const item = this.getItem(ability, types, moves, counter, species);
471
- const levelScale = {
472
- Uber: 76,
473
- OU: 80,
474
- UUBL: 82,
475
- UU: 84,
476
- NUBL: 86,
477
- NU: 88,
478
- NFE: 90,
479
- };
480
- const customScale = {
481
- Ditto: 99, Unown: 99,
482
- };
483
- const tier = species.tier;
484
- const level = this.adjustLevel || customScale[species.name] || levelScale[tier] || (species.nfe ? 90 : 80);
485
- // Prepare optimal HP
486
- let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
487
- if (moves.has('substitute') && ['endeavor', 'flail', 'reversal'].some(m => moves.has(m))) {
488
- // Endeavor/Flail/Reversal users should be able to use four Substitutes
489
- if (hp % 4 === 0)
490
- evs.hp -= 4;
491
- }
492
- else if (moves.has('substitute') && (item === 'Salac Berry' || item === 'Petaya Berry' || item === 'Liechi Berry')) {
493
- // Other pinch berry holders should have berries activate after three Substitutes
494
- while (hp % 4 > 0) {
495
- evs.hp -= 4;
496
- hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
497
- }
498
- }
499
- // Minimize confusion damage
500
- if (!counter.get('Physical') && !moves.has('transform')) {
501
- evs.atk = 0;
502
- ivs.atk = hasHiddenPower ? ivs.atk - 28 : 0;
503
- }
504
- return {
505
- name: species.baseSpecies,
506
- species: forme,
507
- gender: species.gender,
508
- moves: Array.from(moves),
509
- ability: ability,
510
- evs: evs,
511
- ivs: ivs,
512
- item: item,
513
- level,
514
- shiny: this.randomChance(1, 1024),
515
- };
516
- }
517
- randomTeam() {
518
- this.enforceNoDirectCustomBanlistChanges();
519
- const seed = this.prng.seed;
520
- const ruleTable = this.dex.formats.getRuleTable(this.format);
521
- const pokemon = [];
522
- // For Monotype
523
- const isMonotype = !!this.forceMonotype || ruleTable.has('sametypeclause');
524
- const typePool = this.dex.types.names();
525
- const type = this.forceMonotype || this.sample(typePool);
526
- const baseFormes = {};
527
- const tierCount = {};
528
- const typeCount = {};
529
- const typeComboCount = {};
530
- const teamDetails = {};
531
- const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
532
- while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
533
- const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
534
- if (!species.exists || !species.randomBattleMoves)
535
- continue;
536
- // Limit to one of each species (Species Clause)
537
- if (baseFormes[species.baseSpecies])
538
- continue;
539
- // Limit to one Wobbuffet per battle (not just per team)
540
- if (species.name === 'Wobbuffet' && this.battleHasWobbuffet)
541
- continue;
542
- // Limit to one Ditto per battle in Gen 2
543
- if (this.dex.gen < 3 && species.name === 'Ditto' && this.battleHasDitto)
544
- continue;
545
- const tier = species.tier;
546
- const types = species.types;
547
- const typeCombo = types.slice().sort().join();
548
- if (!isMonotype && !this.forceMonotype) {
549
- // Dynamically scale limits for different team sizes. The default and minimum value is 1.
550
- const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
551
- // Limit two Pokemon per tier
552
- if (tierCount[tier] >= 2 * limitFactor)
553
- continue;
554
- // Limit two of any type
555
- let skip = false;
556
- for (const typeName of types) {
557
- if (typeCount[typeName] >= 2 * limitFactor) {
558
- skip = true;
559
- break;
560
- }
561
- }
562
- if (skip)
563
- continue;
564
- // Limit one of any type combination
565
- if (!this.forceMonotype && typeComboCount[typeCombo] >= 1 * limitFactor)
566
- continue;
567
- }
568
- // Okay, the set passes, add it to our team
569
- const set = this.randomSet(species, teamDetails);
570
- pokemon.push(set);
571
- // Now that our Pokemon has passed all checks, we can increment our counters
572
- baseFormes[species.baseSpecies] = 1;
573
- // Increment tier counter
574
- if (tierCount[tier]) {
575
- tierCount[tier]++;
576
- }
577
- else {
578
- tierCount[tier] = 1;
579
- }
580
- // Increment type counters
581
- for (const typeName of types) {
582
- if (typeName in typeCount) {
583
- typeCount[typeName]++;
584
- }
585
- else {
586
- typeCount[typeName] = 1;
587
- }
588
- }
589
- if (typeCombo in typeComboCount) {
590
- typeComboCount[typeCombo]++;
591
- }
592
- else {
593
- typeComboCount[typeCombo] = 1;
594
- }
595
- // Updateeam details
596
- if (set.ability === 'Drizzle' || set.moves.includes('raindance'))
597
- teamDetails.rain = 1;
598
- if (set.ability === 'Sand Stream')
599
- teamDetails.sand = 1;
600
- if (set.moves.includes('spikes'))
601
- teamDetails.spikes = 1;
602
- if (set.moves.includes('rapidspin'))
603
- teamDetails.rapidSpin = 1;
604
- if (set.moves.includes('aromatherapy') || set.moves.includes('healbell'))
605
- teamDetails.statusCure = 1;
606
- // In Gen 3, Shadow Tag users can prevent each other from switching out, possibly causing and endless battle or at least causing a long stall war
607
- // To prevent this, we prevent more than one Wobbuffet in a single battle.
608
- if (set.ability === 'Shadow Tag')
609
- this.battleHasWobbuffet = true;
610
- if (species.id === 'ditto')
611
- this.battleHasDitto = true;
612
- }
613
- if (pokemon.length < this.maxTeamSize && !isMonotype && !this.forceMonotype && pokemon.length < 12) {
614
- throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
615
- }
616
- return pokemon;
617
- }
618
- }
619
- exports.RandomGen3Teams = RandomGen3Teams;
620
- exports.default = RandomGen3Teams;
621
- //# sourceMappingURL=gen3.js.map