@pkmn/randoms 0.6.3 → 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/gen4.js DELETED
@@ -1,724 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RandomGen4Teams = void 0;
4
- const gen5_1 = require("./gen5");
5
- const utils_1 = require("./utils");
6
- const sim_1 = require("@pkmn/sim");
7
- // These moves can be used even if we aren't setting up to use them:
8
- const SetupException = ['dracometeor', 'overheat'];
9
- // Give recovery moves priority over certain other defensive status moves
10
- const recoveryMoves = [
11
- 'healorder', 'milkdrink', 'moonlight', 'morningsun', 'painsplit', 'recover', 'rest', 'roost',
12
- 'slackoff', 'softboiled', 'synthesis', 'wish',
13
- ];
14
- const defensiveStatusMoves = ['aromatherapy', 'haze', 'healbell', 'roar', 'whirlwind', 'willowisp', 'yawn'];
15
- class RandomGen4Teams extends gen5_1.RandomGen5Teams {
16
- constructor(dex, format, prng) {
17
- super(dex, format, prng);
18
- this.moveEnforcementCheckers = {
19
- Bug: (movePool, moves, abilities, types, counter) => (!counter.get('Bug') && (movePool.includes('bugbuzz') || movePool.includes('megahorn'))),
20
- Dark: (movePool, moves, abilities, types, counter) => ((!counter.get('Dark') || (counter.get('Dark') === 1 && moves.has('pursuit'))) &&
21
- movePool.includes('suckerpunch') &&
22
- counter.setupType !== 'Special'),
23
- Dragon: (movePool, moves, abilities, types, counter) => !counter.get('Dragon'),
24
- Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
25
- Fighting: (movePool, moves, abilities, types, counter) => (!counter.get('Fighting') &&
26
- (!!counter.setupType || !counter.get('Status') || movePool.includes('closecombat') || movePool.includes('highjumpkick'))),
27
- Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
28
- Flying: (movePool, moves, abilities, types, counter) => !counter.get('Flying') && ((counter.setupType !== 'Special' && movePool.includes('bravebird')) ||
29
- (abilities.has('Serene Grace') && movePool.includes('airslash'))),
30
- Grass: (movePool, moves, abilities, types, counter) => (!counter.get('Grass') &&
31
- ['leafblade', 'leafstorm', 'seedflare', 'woodhammer'].some(m => movePool.includes(m))),
32
- Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
33
- Ice: (movePool, moves, abilities, types, counter) => (!counter.get('Ice') && (!types.has('Water') || !counter.get('Water'))),
34
- Rock: (movePool, moves, abilities, types, counter) => (!counter.get('Rock') && (movePool.includes('headsmash') || movePool.includes('stoneedge'))),
35
- Steel: (movePool, moves, abilities, types, counter) => !counter.get('Steel') && movePool.includes('meteormash'),
36
- Water: (movePool, moves, abilities, types, counter) => (!counter.get('Water') && (moves.has('raindance') || !types.has('Ice') || !counter.get('Ice'))),
37
- Adaptability: (movePool, moves, abilities, types, counter, species) => (!counter.setupType &&
38
- species.types.length > 1 &&
39
- (!counter.get(species.types[0]) || !counter.get(species.types[1]))),
40
- Guts: (movePool, moves, abilities, types) => types.has('Normal') && movePool.includes('facade'),
41
- 'Slow Start': movePool => movePool.includes('substitute'),
42
- };
43
- }
44
- shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species, isLead) {
45
- const restTalk = moves.has('rest') && moves.has('sleeptalk');
46
- switch (move.id) {
47
- // Not very useful without their supporting moves
48
- case 'batonpass':
49
- return { cull: !counter.setupType && !counter.get('speedsetup') && !moves.has('substitute') };
50
- case 'eruption':
51
- case 'waterspout':
52
- return { cull: counter.get('Physical') + counter.get('Special') < 4 };
53
- case 'focuspunch':
54
- return { cull: !moves.has('substitute') || counter.damagingMoves.size < 2 || moves.has('hammerarm') };
55
- case 'raindance':
56
- return { cull: abilities.has('Hydration') ? !moves.has('rest') : counter.get('Physical') + counter.get('Special') < 2 };
57
- case 'refresh':
58
- return { cull: !(moves.has('calmmind') && (moves.has('recover') || moves.has('roost'))) };
59
- case 'rest':
60
- return { cull: movePool.includes('sleeptalk') || (abilities.has('Hydration') && !moves.has('raindance')) };
61
- case 'sleeptalk':
62
- if (movePool.length > 1) {
63
- const rest = movePool.indexOf('rest');
64
- if (rest >= 0)
65
- this.fastPop(movePool, rest);
66
- }
67
- return { cull: !moves.has('rest') };
68
- case 'sunnyday':
69
- return { cull: !moves.has('solarbeam') };
70
- case 'weatherball':
71
- return { cull: !moves.has('raindance') && !moves.has('sunnyday') };
72
- // Set up once and only if we have the moves for it
73
- case 'bellydrum':
74
- case 'bulkup':
75
- case 'curse':
76
- case 'dragondance':
77
- case 'swordsdance':
78
- const notEnoughPhysicalMoves = (counter.get('Physical') + counter.get('physicalpool') < 2 &&
79
- !moves.has('batonpass') &&
80
- (!moves.has('rest') || !moves.has('sleeptalk')));
81
- const badPhysicalMoveset = counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1;
82
- return { cull: moves.has('sunnyday') || notEnoughPhysicalMoves || badPhysicalMoveset, isSetup: true };
83
- case 'calmmind':
84
- case 'nastyplot':
85
- case 'tailglow':
86
- const notEnoughSpecialMoves = (counter.get('Special') + counter.get('specialpool') < 2 &&
87
- !moves.has('batonpass') &&
88
- (!moves.has('rest') || !moves.has('sleeptalk')));
89
- const badSpecialMoveset = counter.setupType !== 'Special' || counter.get('specialsetup') > 1;
90
- return { cull: notEnoughSpecialMoves || badSpecialMoveset, isSetup: true };
91
- case 'agility':
92
- case 'rockpolish':
93
- return { cull: restTalk || (counter.damagingMoves.size < 2 && !moves.has('batonpass')), isSetup: !counter.setupType };
94
- // Bad after setup
95
- case 'destinybond':
96
- return { cull: !!counter.setupType || moves.has('explosion') };
97
- case 'explosion':
98
- case 'selfdestruct':
99
- return { cull: (counter.setupType === 'Special' ||
100
- Array.from(moves).some(id => recoveryMoves.includes(id) || defensiveStatusMoves.includes(id)) ||
101
- ['batonpass', 'protect', 'substitute'].some(m => moves.has(m))) };
102
- case 'foresight':
103
- case 'roar':
104
- case 'whirlwind':
105
- return { cull: !!counter.setupType && !abilities.has('Speed Boost') };
106
- case 'healingwish':
107
- case 'lunardance':
108
- return { cull: !!counter.setupType || moves.has('rest') || moves.has('substitute') };
109
- case 'protect':
110
- return { cull: (!['Guts', 'Quick Feet', 'Speed Boost'].some(abil => abilities.has(abil)) ||
111
- ['rest', 'softboiled', 'toxic', 'wish'].some(m => moves.has(m))) };
112
- case 'wish':
113
- return { cull: (!['batonpass', 'protect', 'uturn'].some(m => moves.has(m)) ||
114
- moves.has('rest') ||
115
- !!counter.get('speedsetup')) };
116
- case 'rapidspin':
117
- return { cull: !!teamDetails.rapidSpin || (!!counter.setupType && counter.get('Physical') + counter.get('Special') < 2) };
118
- case 'reflect':
119
- case 'lightscreen':
120
- case 'fakeout':
121
- return { cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('substitute') };
122
- case 'spikes':
123
- return { cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('substitute') };
124
- case 'stealthrock':
125
- return { cull: (!!counter.setupType ||
126
- !!counter.get('speedsetup') ||
127
- moves.has('rest') || moves.has('substitute') ||
128
- !!teamDetails.stealthRock) };
129
- case 'switcheroo':
130
- case 'trick':
131
- return { cull: (counter.get('Physical') + counter.get('Special') < 3 ||
132
- !!counter.setupType ||
133
- ['fakeout', 'lightscreen', 'reflect', 'suckerpunch', 'trickroom'].some(m => moves.has(m))) };
134
- case 'toxic':
135
- case 'toxicspikes':
136
- return { cull: (!!counter.setupType || !!counter.get('speedsetup') || !!teamDetails.toxicSpikes || moves.has('willowisp')) };
137
- case 'trickroom':
138
- return { cull: (!!counter.setupType ||
139
- !!counter.get('speedsetup') ||
140
- counter.damagingMoves.size < 2 ||
141
- moves.has('lightscreen') || moves.has('reflect') ||
142
- restTalk) };
143
- case 'uturn':
144
- return { cull: ((types.has('Bug') && counter.get('stab') < 2 && counter.damagingMoves.size > 1) ||
145
- (abilities.has('Speed Boost') && moves.has('protect')) ||
146
- !!counter.setupType ||
147
- !!counter.get('speedsetup') ||
148
- moves.has('batonpass') || moves.has('substitute')) };
149
- // Bit redundant to have both
150
- // Attacks:
151
- case 'bodyslam':
152
- case 'slash':
153
- return { cull: moves.has('facade') || moves.has('return') };
154
- case 'doubleedge':
155
- return { cull: ['bodyslam', 'facade', 'return'].some(m => moves.has(m)) };
156
- case 'endeavor':
157
- return { cull: !isLead };
158
- case 'headbutt':
159
- return { cull: !moves.has('bodyslam') && !moves.has('thunderwave') };
160
- case 'judgment':
161
- case 'swift':
162
- return { cull: counter.setupType !== 'Special' && counter.get('stab') > 1 };
163
- case 'quickattack':
164
- return { cull: moves.has('thunderwave') };
165
- case 'firepunch':
166
- case 'flamethrower':
167
- return { cull: moves.has('fireblast') || moves.has('overheat') && !counter.setupType };
168
- case 'lavaplume':
169
- case 'fireblast':
170
- if (move.id === 'fireblast' && moves.has('lavaplume') && !counter.get('speedsetup'))
171
- return { cull: true };
172
- if (move.id === 'lavaplume' && moves.has('fireblast') && counter.get('speedsetup'))
173
- return { cull: true };
174
- if (moves.has('flareblitz') && counter.setupType !== 'Special')
175
- return { cull: true };
176
- break;
177
- case 'overheat':
178
- return { cull: counter.setupType === 'Special' || ['batonpass', 'fireblast', 'flareblitz'].some(m => moves.has(m)) };
179
- case 'aquajet':
180
- return { cull: moves.has('dragondance') || (moves.has('waterfall') && counter.get('Physical') < 3) };
181
- case 'hydropump':
182
- return { cull: moves.has('surf') };
183
- case 'waterfall':
184
- return { cull: (moves.has('aquatail') ||
185
- (counter.setupType !== 'Physical' && (moves.has('hydropump') || moves.has('surf')))) };
186
- case 'chargebeam':
187
- return { cull: moves.has('thunderbolt') && counter.get('Special') < 3 };
188
- case 'discharge':
189
- return { cull: moves.has('thunderbolt') };
190
- case 'energyball':
191
- return { cull: (moves.has('leafblade') ||
192
- moves.has('woodhammer') ||
193
- (moves.has('sunnyday') && moves.has('solarbeam')) ||
194
- (moves.has('leafstorm') && counter.get('Physical') + counter.get('Special') < 4)) };
195
- case 'grassknot':
196
- case 'leafblade':
197
- case 'seedbomb':
198
- return { cull: moves.has('woodhammer') || (moves.has('sunnyday') && moves.has('solarbeam')) };
199
- case 'leafstorm':
200
- return { cull: (!!counter.setupType ||
201
- moves.has('batonpass') ||
202
- moves.has('powerwhip') ||
203
- (moves.has('sunnyday') && moves.has('solarbeam'))) };
204
- case 'solarbeam':
205
- return { cull: counter.setupType === 'Physical' || !moves.has('sunnyday') };
206
- case 'icepunch':
207
- return { cull: !counter.setupType && moves.has('icebeam') };
208
- case 'aurasphere':
209
- case 'drainpunch':
210
- case 'focusblast':
211
- return { cull: moves.has('closecombat') && counter.setupType !== 'Special' };
212
- case 'brickbreak':
213
- case 'closecombat':
214
- case 'crosschop':
215
- case 'lowkick':
216
- return { cull: moves.has('substitute') && moves.has('focuspunch') };
217
- case 'machpunch':
218
- return { cull: (counter.damagingMoves.size <= counter.get('Fighting') ||
219
- (types.has('Fighting') && counter.get('stab') < 2 && !abilities.has('Technician'))) };
220
- case 'seismictoss':
221
- return { cull: moves.has('nightshade') || counter.get('Physical') + counter.get('Special') >= 1 };
222
- case 'superpower':
223
- return { cull: moves.has('dragondance') || !!counter.get('speedsetup') };
224
- case 'gunkshot':
225
- return { cull: moves.has('poisonjab') };
226
- case 'earthpower':
227
- return { cull: moves.has('earthquake') };
228
- case 'airslash':
229
- return { cull: !counter.setupType && moves.has('bravebird') };
230
- case 'zenheadbutt':
231
- return { cull: moves.has('psychocut') };
232
- case 'rockblast':
233
- case 'rockslide':
234
- return { cull: moves.has('stoneedge') };
235
- case 'shadowclaw':
236
- return { cull: moves.has('shadowforce') || moves.has('suckerpunch') && !types.has('Ghost') };
237
- case 'dracometeor':
238
- return { cull: moves.has('calmmind') || restTalk || (!!counter.setupType && counter.get('stab') < 2) };
239
- case 'dragonclaw':
240
- return { cull: moves.has('outrage') };
241
- case 'dragonpulse':
242
- return { cull: moves.has('dracometeor') && moves.has('outrage') };
243
- case 'crunch':
244
- case 'nightslash':
245
- return { cull: moves.has('suckerpunch') };
246
- case 'pursuit':
247
- return { cull: !!counter.setupType || moves.has('payback') };
248
- case 'flashcannon':
249
- return { cull: (moves.has('ironhead') || movePool.includes('ironhead')) && counter.setupType !== 'Special' };
250
- // Status:
251
- case 'encore':
252
- return { cull: ['roar', 'taunt', 'whirlwind'].some(m => moves.has(m)) || restTalk };
253
- case 'haze':
254
- case 'taunt':
255
- return { cull: restTalk };
256
- case 'leechseed':
257
- case 'painsplit':
258
- return { cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('rest') };
259
- case 'recover':
260
- case 'slackoff':
261
- return { cull: restTalk };
262
- case 'stunspore':
263
- return { cull: (!!counter.setupType ||
264
- moves.has('toxic') ||
265
- movePool.includes('sleeppowder') ||
266
- movePool.includes('spore')) };
267
- case 'substitute':
268
- return { cull: ['pursuit', 'rapidspin', 'rest', 'taunt'].some(m => moves.has(m)) };
269
- case 'thunderwave':
270
- return { cull: (!!counter.setupType ||
271
- moves.has('toxic') ||
272
- moves.has('trickroom') ||
273
- (moves.has('bodyslam') && abilities.has('Serene Grace'))) };
274
- case 'yawn':
275
- return { cull: moves.has('thunderwave') || moves.has('toxic') };
276
- }
277
- return { cull: false };
278
- }
279
- shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species) {
280
- switch (ability) {
281
- case 'Anger Point':
282
- case 'Ice Body':
283
- case 'Steadfast':
284
- return true;
285
- case 'Blaze':
286
- return !counter.get('Fire');
287
- case 'Chlorophyll':
288
- return !moves.has('sunnyday') && !teamDetails.sun;
289
- case 'Compound Eyes':
290
- case 'No Guard':
291
- return !counter.get('inaccurate');
292
- case 'Early Bird':
293
- return !moves.has('rest');
294
- case 'Gluttony':
295
- return !moves.has('bellydrum');
296
- case 'Hustle':
297
- return counter.get('Physical') < 2;
298
- case 'Mold Breaker':
299
- return !moves.has('earthquake');
300
- case 'Overgrow':
301
- return !counter.get('Grass');
302
- case 'Reckless':
303
- case 'Rock Head':
304
- return !counter.get('recoil');
305
- case 'Sand Veil':
306
- return !teamDetails.sand;
307
- case 'Serene Grace':
308
- return !counter.get('serenegrace') || species.id === 'blissey';
309
- case 'Simple':
310
- return !counter.setupType && !moves.has('cosmicpower');
311
- case 'Skill Link':
312
- return !counter.get('skilllink');
313
- case 'Snow Cloak':
314
- return !teamDetails.hail;
315
- case 'Solar Power':
316
- return !counter.get('Special') || !moves.has('sunnyday') && !teamDetails.sun;
317
- case 'Speed Boost':
318
- return moves.has('uturn');
319
- case 'Swift Swim':
320
- return !moves.has('raindance') && !teamDetails.rain;
321
- case 'Swarm':
322
- return !counter.get('Bug');
323
- case 'Synchronize':
324
- return counter.get('Status') < 2;
325
- case 'Technician':
326
- return !counter.get('technician') || moves.has('toxic');
327
- case 'Tinted Lens':
328
- return counter.get('damage') >= counter.damagingMoves.size;
329
- case 'Torrent':
330
- return !counter.get('Water');
331
- }
332
- return false;
333
- }
334
- getHighPriorityItem(ability, types, moves, counter, teamDetails, species, isLead) {
335
- if (species.requiredItem)
336
- return species.requiredItem;
337
- if (species.requiredItems)
338
- return this.sample(species.requiredItems);
339
- if (species.name === 'Farfetch\u2019d')
340
- return 'Stick';
341
- if (species.name === 'Marowak')
342
- return 'Thick Club';
343
- if (species.name === 'Shedinja' || species.name === 'Smeargle')
344
- return 'Focus Sash';
345
- if (species.name === 'Unown')
346
- return 'Choice Specs';
347
- if (species.name === 'Wobbuffet') {
348
- return moves.has('destinybond') ? 'Custap Berry' : this.sample(['Leftovers', 'Sitrus Berry']);
349
- }
350
- if (moves.has('switcheroo') || moves.has('trick')) {
351
- if (species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
352
- !counter.get('priority') &&
353
- this.randomChance(2, 3)) {
354
- return 'Choice Scarf';
355
- }
356
- else {
357
- return (counter.get('Physical') > counter.get('Special')) ? 'Choice Band' : 'Choice Specs';
358
- }
359
- }
360
- if (moves.has('bellydrum'))
361
- return 'Sitrus Berry';
362
- if (ability === 'Magic Guard' || ability === 'Speed Boost' && counter.get('Status') < 2)
363
- return 'Life Orb';
364
- if (ability === 'Poison Heal' || ability === 'Toxic Boost')
365
- return 'Toxic Orb';
366
- if (moves.has('rest') && !moves.has('sleeptalk') && ability !== 'Natural Cure' && ability !== 'Shed Skin') {
367
- return (moves.has('raindance') && ability === 'Hydration') ? 'Damp Rock' : 'Chesto Berry';
368
- }
369
- if (moves.has('raindance') && ability === 'Swift Swim' && counter.get('Status') < 2)
370
- return 'Life Orb';
371
- if (moves.has('sunnyday'))
372
- return (ability === 'Chlorophyll' && counter.get('Status') < 2) ? 'Life Orb' : 'Heat Rock';
373
- if (moves.has('lightscreen') && moves.has('reflect'))
374
- return 'Light Clay';
375
- if ((ability === 'Guts' || ability === 'Quick Feet') && moves.has('facade'))
376
- return 'Toxic Orb';
377
- if (ability === 'Unburden')
378
- return 'Sitrus Berry';
379
- if (species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 150) {
380
- return isLead ? 'Focus Sash' : 'Life Orb';
381
- }
382
- if (moves.has('endeavor'))
383
- return 'Focus Sash';
384
- }
385
- getMediumPriorityItem(ability, moves, counter, species, isDoubles, isLead) {
386
- if (ability === 'Slow Start' ||
387
- ['curse', 'leechseed', 'protect', 'roar', 'sleeptalk', 'whirlwind'].some(m => moves.has(m)) ||
388
- (ability === 'Serene Grace' && ['bodyslam', 'headbutt', 'ironhead'].some(m => moves.has(m)))) {
389
- return 'Leftovers';
390
- }
391
- if (counter.get('Physical') >= 4 && !moves.has('fakeout') && !moves.has('rapidspin') && !moves.has('suckerpunch')) {
392
- return (species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
393
- !counter.get('priority') && !moves.has('bodyslam') && this.randomChance(2, 3)) ? 'Choice Scarf' : 'Choice Band';
394
- }
395
- if ((counter.get('Special') >= 4 || (counter.get('Special') >= 3 &&
396
- ['batonpass', 'uturn', 'waterspout', 'selfdestruct'].some(m => moves.has(m)))) &&
397
- !moves.has('chargebeam')) {
398
- return (species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
399
- ability !== 'Speed Boost' && !counter.get('priority') && this.randomChance(2, 3)) ? 'Choice Scarf' : 'Choice Specs';
400
- }
401
- if (moves.has('outrage') && counter.setupType)
402
- return 'Lum Berry';
403
- if (moves.has('substitute')) {
404
- return (counter.damagingMoves.size < 2 ||
405
- !counter.get('drain') &&
406
- (counter.damagingMoves.size < 3 || species.baseStats.hp >= 60 || species.baseStats.def + species.baseStats.spd >= 180)) ? 'Leftovers' : 'Life Orb';
407
- }
408
- if (ability === 'Guts')
409
- return 'Toxic Orb';
410
- if (isLead &&
411
- !counter.get('recoil') &&
412
- !Array.from(moves).some(id => !!recoveryMoves.includes(id)) &&
413
- species.baseStats.hp + species.baseStats.def + species.baseStats.spd < 225) {
414
- return 'Focus Sash';
415
- }
416
- if (counter.damagingMoves.size >= 4) {
417
- return (counter.get('Normal') || counter.get('Dragon') > 1 || moves.has('chargebeam') || moves.has('suckerpunch')) ? 'Life Orb' : 'Expert Belt';
418
- }
419
- if (counter.damagingMoves.size >= 3 && !moves.has('superfang') && !moves.has('metalburst')) {
420
- const totalBulk = species.baseStats.hp + species.baseStats.def + species.baseStats.spd;
421
- return (counter.get('speedsetup') || counter.get('priority') ||
422
- moves.has('dragondance') || moves.has('trickroom') ||
423
- totalBulk < 235 ||
424
- (species.baseStats.spe >= 70 && (totalBulk < 260 || (!!counter.get('recovery') && totalBulk < 285)))) ? 'Life Orb' : 'Leftovers';
425
- }
426
- }
427
- getLowPriorityItem(ability, types, moves, abilities, counter, teamDetails, species) {
428
- if (types.has('Poison'))
429
- return 'Black Sludge';
430
- if (this.dex.getEffectiveness('Rock', species) >= 1 || moves.has('roar'))
431
- return 'Leftovers';
432
- if (counter.get('Status') <= 1 && ['metalburst', 'rapidspin', 'superfang'].every(m => !moves.has(m)))
433
- return 'Life Orb';
434
- return 'Leftovers';
435
- }
436
- randomSet(species, teamDetails = {}, isLead = false) {
437
- species = this.dex.species.get(species);
438
- let forme = species.name;
439
- if (typeof species.battleOnly === 'string')
440
- forme = species.battleOnly;
441
- if (species.cosmeticFormes) {
442
- forme = this.sample([species.name].concat(species.cosmeticFormes));
443
- }
444
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id))).slice();
445
- const rejectedPool = [];
446
- const moves = new Set();
447
- let ability = '';
448
- let item;
449
- const evs = { hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85 };
450
- const ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
451
- const types = new Set(species.types);
452
- const abilities = new Set(Object.values(species.abilities));
453
- let availableHP = 0;
454
- for (const setMoveid of movePool) {
455
- if (setMoveid.startsWith('hiddenpower'))
456
- availableHP++;
457
- }
458
- let counter;
459
- let hasHiddenPower = false;
460
- do {
461
- // Choose next 4 moves from learnset/viable moves and add them to moves list:
462
- while (moves.size < this.maxMoveCount && movePool.length) {
463
- const moveid = this.sampleNoReplace(movePool);
464
- if (moveid.startsWith('hiddenpower')) {
465
- availableHP--;
466
- if (hasHiddenPower)
467
- continue;
468
- hasHiddenPower = true;
469
- }
470
- moves.add(moveid);
471
- }
472
- while (moves.size < this.maxMoveCount && rejectedPool.length) {
473
- const moveid = this.sampleNoReplace(rejectedPool);
474
- if (moveid.startsWith('hiddenpower')) {
475
- if (hasHiddenPower)
476
- continue;
477
- hasHiddenPower = true;
478
- }
479
- moves.add(moveid);
480
- }
481
- counter = this.queryMoves(moves, species.types, abilities, movePool);
482
- if (types.has('Dark') && moves.has('suckerpunch') && species.types.length === 1) {
483
- counter.add('stab');
484
- }
485
- // Iterate through the moves again, this time to cull them:
486
- for (const moveid of moves) {
487
- const move = this.dex.moves.get(moveid);
488
- let { cull, isSetup } = this.shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species, isLead);
489
- // Increased/decreased priority moves are unneeded with moves that boost only speed
490
- if (move.priority !== 0 && !!counter.get('speedsetup'))
491
- cull = true;
492
- // This move doesn't satisfy our setup requirements:
493
- if ((move.category === 'Physical' && counter.setupType === 'Special') ||
494
- (move.category === 'Special' && counter.setupType === 'Physical')) {
495
- // Reject STABs last in case the setup type changes later on
496
- if (!SetupException.includes(moveid) &&
497
- (!types.has(move.type) || counter.get('stab') > 1 || counter.get(move.category) < 2)) {
498
- cull = true;
499
- }
500
- }
501
- if (counter.setupType && !isSetup && move.category !== counter.setupType &&
502
- counter.get(counter.setupType) < 2 && !moves.has('batonpass')) {
503
- // Mono-attacking with setup and RestTalk or recovery + status healing is allowed
504
- if (moveid !== 'rest' && moveid !== 'sleeptalk' &&
505
- !(recoveryMoves.includes(moveid) && (moves.has('healbell') || moves.has('refresh'))) &&
506
- !((moveid === 'healbell' || moveid === 'refresh') && Array.from(moves).some(id => recoveryMoves.includes(id))) && (
507
- // Reject Status moves only if there is nothing else to reject
508
- move.category !== 'Status' || (counter.get(counter.setupType) + counter.get('Status') > 3 &&
509
- counter.get('physicalsetup') + counter.get('specialsetup') < 2))) {
510
- cull = true;
511
- }
512
- }
513
- if (moveid === 'hiddenpower' &&
514
- counter.setupType === 'Special' &&
515
- species.types.length > 1 &&
516
- counter.get('Special') <= 2 &&
517
- !types.has(move.type) &&
518
- !counter.get('Physical') &&
519
- counter.get('specialpool') &&
520
- (!(types.has('Ghost') && move.type === 'Fighting' || types.has('Electric') && move.type === 'Ice'))) {
521
- // Hidden Power isn't good enough
522
- cull = true;
523
- }
524
- // Reject defensive status moves if a reliable recovery move is available but not selected.
525
- // Toxic is only defensive if used with another status move other than Protect (Toxic + 3 attacks and Toxic + Protect are ok).
526
- if (!Array.from(moves).some(id => recoveryMoves.includes(id)) &&
527
- movePool.some(id => recoveryMoves.includes(id)) && (defensiveStatusMoves.includes(moveid) ||
528
- (moveid === 'toxic' && ((counter.get('Status') > 1 && !moves.has('protect')) || counter.get('Status') > 2)))) {
529
- cull = true;
530
- }
531
- const runEnforcementChecker = (checkerName) => {
532
- if (!this.moveEnforcementCheckers[checkerName])
533
- return false;
534
- return this.moveEnforcementCheckers[checkerName](movePool, moves, abilities, types, counter, species, teamDetails);
535
- };
536
- const moveIsRejectable = (!move.weather &&
537
- !move.damage &&
538
- (move.category !== 'Status' || !move.flags.heal) &&
539
- (move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40 && !move.multihit)) &&
540
- // These moves cannot be rejected in favor of a forced move
541
- !['judgment', 'sleeptalk'].includes(moveid) &&
542
- // Setup-supported moves should only be rejected under specific circumstances
543
- (counter.get('physicalsetup') + counter.get('specialsetup') < 2 && (!counter.setupType || counter.setupType === 'Mixed' ||
544
- (move.category !== counter.setupType && move.category !== 'Status') ||
545
- counter.get(counter.setupType) + counter.get('Status') > 3)));
546
- if (!cull && !isSetup && moveIsRejectable) {
547
- // There may be more important moves that this Pokemon needs
548
- const canRollForcedMoves = (
549
- // These moves should always be rolled
550
- movePool.includes('spore') || (!Array.from(moves).some(id => recoveryMoves.includes(id)) && (movePool.includes('softboiled') ||
551
- (species.baseSpecies === 'Arceus' && movePool.includes('recover')))));
552
- // Pokemon should usually have at least one STAB move
553
- const requiresStab = (!counter.get('stab') && !counter.get('damage') && (species.types.length > 1 ||
554
- (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') ||
555
- !moves.has('icebeam') ||
556
- species.baseStats.spa >= species.baseStats.spd));
557
- if (canRollForcedMoves ||
558
- requiresStab ||
559
- (species.requiredMove && movePool.includes((0, sim_1.toID)(species.requiredMove))) ||
560
- (counter.get('defensesetup') && !counter.get('recovery') && !moves.has('rest'))) {
561
- cull = true;
562
- }
563
- else {
564
- // Pokemon should have moves that benefit their typing or ability
565
- for (const type of types) {
566
- if (runEnforcementChecker(type))
567
- cull = true;
568
- }
569
- for (const abil of abilities) {
570
- if (runEnforcementChecker(abil))
571
- cull = true;
572
- }
573
- }
574
- }
575
- // Sleep Talk shouldn't be selected without Rest
576
- if (moveid === 'rest' && cull) {
577
- const sleeptalk = movePool.indexOf('sleeptalk');
578
- if (sleeptalk >= 0) {
579
- if (movePool.length < 2) {
580
- cull = false;
581
- }
582
- else {
583
- this.fastPop(movePool, sleeptalk);
584
- }
585
- }
586
- }
587
- // Remove rejected moves from the move list
588
- if (cull && (movePool.length - availableHP || availableHP && (moveid.startsWith('hiddenpower') || !hasHiddenPower))) {
589
- if (move.category !== 'Status' && (!moveid.startsWith('hiddenpower') || !availableHP))
590
- rejectedPool.push(moveid);
591
- moves.delete(moveid);
592
- if (moveid.startsWith('hiddenpower'))
593
- hasHiddenPower = false;
594
- break;
595
- }
596
- if (cull && rejectedPool.length) {
597
- moves.delete(moveid);
598
- if (moveid.startsWith('hiddenpower'))
599
- hasHiddenPower = false;
600
- break;
601
- }
602
- }
603
- } while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
604
- if (hasHiddenPower) {
605
- let hpType;
606
- for (const move of moves) {
607
- if (move.startsWith('hiddenpower')) {
608
- hpType = move.substr(11);
609
- break;
610
- }
611
- }
612
- if (!hpType)
613
- throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
614
- const HPivs = this.dex.types.get(hpType).HPivs;
615
- let iv;
616
- for (iv in HPivs) {
617
- ivs[iv] = HPivs[iv];
618
- }
619
- }
620
- const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a));
621
- utils_1.Utils.sortBy(abilityData, abil => -abil.rating);
622
- let ability0 = abilityData[0];
623
- let ability1 = abilityData[1];
624
- if (abilityData[1]) {
625
- if (ability0.rating <= ability1.rating && this.randomChance(1, 2)) {
626
- [ability0, ability1] = [ability1, ability0];
627
- }
628
- else if (ability0.rating - 0.6 <= ability1.rating && this.randomChance(2, 3)) {
629
- [ability0, ability1] = [ability1, ability0];
630
- }
631
- ability = ability0.name;
632
- while (this.shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species)) {
633
- if (ability === ability0.name && ability1.rating >= 1) {
634
- ability = ability1.name;
635
- }
636
- else {
637
- // Default to the highest rated ability if all are rejected
638
- ability = abilityData[0].name;
639
- break;
640
- }
641
- }
642
- if (abilities.has('Hydration') && moves.has('raindance') && moves.has('rest')) {
643
- ability = 'Hydration';
644
- }
645
- else if (abilities.has('Swift Swim') && moves.has('raindance')) {
646
- ability = 'Swift Swim';
647
- }
648
- else if (abilities.has('Technician') && moves.has('machpunch') && types.has('Fighting') && counter.get('stab') < 2) {
649
- ability = 'Technician';
650
- }
651
- }
652
- else {
653
- ability = ability0.name;
654
- }
655
- item = this.getHighPriorityItem(ability, types, moves, counter, teamDetails, species, isLead);
656
- if (item === undefined)
657
- item = this.getMediumPriorityItem(ability, moves, counter, species, false, isLead);
658
- if (item === undefined) {
659
- item = this.getLowPriorityItem(ability, types, moves, abilities, counter, teamDetails, species);
660
- }
661
- // For Trick / Switcheroo
662
- if (item === 'Leftovers' && types.has('Poison')) {
663
- item = 'Black Sludge';
664
- }
665
- const levelScale = {
666
- AG: 74,
667
- Uber: 76,
668
- OU: 80,
669
- '(OU)': 82,
670
- UUBL: 82,
671
- UU: 84,
672
- NUBL: 86,
673
- NU: 88,
674
- };
675
- const customScale = {
676
- Delibird: 100, Ditto: 100, 'Farfetch\u2019d': 100, Unown: 100, Castform: 100,
677
- };
678
- const level = this.adjustLevel || customScale[species.name] || levelScale[species.tier] || (species.nfe ? 90 : 80);
679
- // Prepare optimal HP
680
- let hp = Math.floor(Math.floor(2 * species.baseStats.hp + (ivs.hp || 31) + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
681
- if (moves.has('substitute') && item === 'Sitrus Berry') {
682
- // Two Substitutes should activate Sitrus Berry
683
- while (hp % 4 > 0) {
684
- evs.hp -= 4;
685
- hp = Math.floor(Math.floor(2 * species.baseStats.hp + (ivs.hp || 31) + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
686
- }
687
- }
688
- else if (moves.has('bellydrum') && item === 'Sitrus Berry') {
689
- // Belly Drum should activate Sitrus Berry
690
- if (hp % 2 > 0)
691
- evs.hp -= 4;
692
- }
693
- else {
694
- // Maximize number of Stealth Rock switch-ins
695
- const srWeakness = this.dex.getEffectiveness('Rock', species);
696
- if (srWeakness > 0 && hp % (4 / srWeakness) === 0)
697
- evs.hp -= 4;
698
- }
699
- // Minimize confusion damage
700
- if (!counter.get('Physical') && !moves.has('transform')) {
701
- evs.atk = 0;
702
- ivs.atk = hasHiddenPower ? (ivs.atk || 31) - 28 : 0;
703
- }
704
- if (['gyroball', 'metalburst', 'trickroom'].some(m => moves.has(m))) {
705
- evs.spe = 0;
706
- ivs.spe = hasHiddenPower ? (ivs.spe || 31) - 28 : 0;
707
- }
708
- return {
709
- name: species.baseSpecies,
710
- species: forme,
711
- gender: species.gender,
712
- shiny: this.randomChance(1, 1024),
713
- moves: Array.from(moves),
714
- ability,
715
- evs,
716
- ivs,
717
- item,
718
- level,
719
- };
720
- }
721
- }
722
- exports.RandomGen4Teams = RandomGen4Teams;
723
- exports.default = RandomGen4Teams;
724
- //# sourceMappingURL=gen4.js.map