@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/index.d.ts +5 -3
- package/build/index.js +8036 -33
- package/build/index.js.map +1 -1
- package/build/index.mjs +8039 -0
- package/build/index.mjs.map +1 -0
- package/package.json +8 -9
- package/build/gen1.d.ts +0 -43
- package/build/gen1.js +0 -447
- package/build/gen1.js.map +0 -1
- package/build/gen2.d.ts +0 -13
- package/build/gen2.js +0 -289
- package/build/gen2.js.map +0 -1
- package/build/gen3.d.ts +0 -17
- package/build/gen3.js +0 -621
- package/build/gen3.js.map +0 -1
- package/build/gen4.d.ts +0 -16
- package/build/gen4.js +0 -724
- package/build/gen4.js.map +0 -1
- package/build/gen5.d.ts +0 -16
- package/build/gen5.js +0 -842
- package/build/gen5.js.map +0 -1
- package/build/gen6.d.ts +0 -23
- package/build/gen6.js +0 -1216
- package/build/gen6.js.map +0 -1
- package/build/gen7.d.ts +0 -43
- package/build/gen7.js +0 -2022
- package/build/gen7.js.map +0 -1
- package/build/gen8.d.ts +0 -154
- package/build/gen8.js +0 -2921
- package/build/gen8.js.map +0 -1
- package/build/utils.d.ts +0 -43
- package/build/utils.js +0 -70
- package/build/utils.js.map +0 -1
- package/src/gen1.ts +0 -493
- package/src/gen2.ts +0 -323
- package/src/gen3.ts +0 -688
- package/src/gen4.ts +0 -858
- package/src/gen5.ts +0 -916
- package/src/gen6.ts +0 -1357
- package/src/gen7.ts +0 -2194
- package/src/gen8.ts +0 -3122
- package/src/global.d.ts +0 -6
- package/src/index.ts +0 -46
- package/src/utils.ts +0 -78
package/src/gen1.ts
DELETED
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
import {MoveCounter} from './gen8';
|
|
2
|
-
import {RandomGen2Teams} from './gen2';
|
|
3
|
-
import {Utils} from './utils';
|
|
4
|
-
import {
|
|
5
|
-
ID,
|
|
6
|
-
Move,
|
|
7
|
-
PokemonSet,
|
|
8
|
-
RandomTeamsTypes,
|
|
9
|
-
Species,
|
|
10
|
-
StatID,
|
|
11
|
-
StatsTable,
|
|
12
|
-
} from '@pkmn/sim';
|
|
13
|
-
|
|
14
|
-
interface HackmonsCupEntry {
|
|
15
|
-
types: string[];
|
|
16
|
-
baseStats: StatsTable;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class RandomGen1Teams extends RandomGen2Teams {
|
|
20
|
-
// Challenge Cup or CC teams are basically fully random teams.
|
|
21
|
-
randomCCTeam() {
|
|
22
|
-
this.enforceNoDirectCustomBanlistChanges();
|
|
23
|
-
|
|
24
|
-
const team = [];
|
|
25
|
-
|
|
26
|
-
const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype);
|
|
27
|
-
|
|
28
|
-
for (const pokemon of randomN) {
|
|
29
|
-
const species = this.dex.species.get(pokemon);
|
|
30
|
-
const learnset = this.dex.species.getLearnset(species.id);
|
|
31
|
-
|
|
32
|
-
// Level balance: calculate directly from stats rather than using some silly lookup table.
|
|
33
|
-
const mbstmin = 1307;
|
|
34
|
-
const stats = species.baseStats;
|
|
35
|
-
|
|
36
|
-
// Modified base stat total assumes 15 DVs, 255 EVs in every stat
|
|
37
|
-
let mbst = (stats["hp"] * 2 + 30 + 63 + 100) + 10;
|
|
38
|
-
mbst += (stats["atk"] * 2 + 30 + 63 + 100) + 5;
|
|
39
|
-
mbst += (stats["def"] * 2 + 30 + 63 + 100) + 5;
|
|
40
|
-
mbst += (stats["spa"] * 2 + 30 + 63 + 100) + 5;
|
|
41
|
-
mbst += (stats["spd"] * 2 + 30 + 63 + 100) + 5;
|
|
42
|
-
mbst += (stats["spe"] * 2 + 30 + 63 + 100) + 5;
|
|
43
|
-
|
|
44
|
-
let level;
|
|
45
|
-
if (this.adjustLevel) {
|
|
46
|
-
level = this.adjustLevel;
|
|
47
|
-
} else {
|
|
48
|
-
level = Math.floor(100 * mbstmin / mbst); // Initial level guess will underestimate
|
|
49
|
-
|
|
50
|
-
while (level < 100) {
|
|
51
|
-
mbst = Math.floor((stats["hp"] * 2 + 30 + 63 + 100) * level / 100 + 10);
|
|
52
|
-
// Since damage is roughly proportional to lvl
|
|
53
|
-
mbst += Math.floor(((stats["atk"] * 2 + 30 + 63 + 100) * level / 100 + 5) * level / 100);
|
|
54
|
-
mbst += Math.floor((stats["def"] * 2 + 30 + 63 + 100) * level / 100 + 5);
|
|
55
|
-
mbst += Math.floor(((stats["spa"] * 2 + 30 + 63 + 100) * level / 100 + 5) * level / 100);
|
|
56
|
-
mbst += Math.floor((stats["spd"] * 2 + 30 + 63 + 100) * level / 100 + 5);
|
|
57
|
-
mbst += Math.floor((stats["spe"] * 2 + 30 + 63 + 100) * level / 100 + 5);
|
|
58
|
-
|
|
59
|
-
if (mbst >= mbstmin) break;
|
|
60
|
-
level++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Random DVs.
|
|
65
|
-
const ivs = {
|
|
66
|
-
hp: 0,
|
|
67
|
-
atk: this.random(16),
|
|
68
|
-
def: this.random(16),
|
|
69
|
-
spa: this.random(16),
|
|
70
|
-
spd: 0,
|
|
71
|
-
spe: this.random(16),
|
|
72
|
-
};
|
|
73
|
-
ivs["hp"] = (ivs["atk"] % 2) * 16 + (ivs["def"] % 2) * 8 + (ivs["spe"] % 2) * 4 + (ivs["spa"] % 2) * 2;
|
|
74
|
-
ivs["atk"] *= 2;
|
|
75
|
-
ivs["def"] *= 2;
|
|
76
|
-
ivs["spa"] *= 2;
|
|
77
|
-
ivs["spd"] = ivs["spa"];
|
|
78
|
-
ivs["spe"] *= 2;
|
|
79
|
-
|
|
80
|
-
// Maxed EVs.
|
|
81
|
-
const evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255};
|
|
82
|
-
|
|
83
|
-
// Four random unique moves from movepool. don't worry about "attacking" or "viable".
|
|
84
|
-
// Since Gens 1 and 2 learnsets are shared, we need to weed out Gen 2 moves.
|
|
85
|
-
const pool: string[] = [];
|
|
86
|
-
if (learnset) {
|
|
87
|
-
for (const move in learnset) {
|
|
88
|
-
if (this.dex.moves.get(move).gen !== 1) continue;
|
|
89
|
-
if (learnset[move].some(learned => learned.startsWith('1'))) {
|
|
90
|
-
pool.push(move);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
team.push({
|
|
96
|
-
name: species.baseSpecies,
|
|
97
|
-
species: species.name,
|
|
98
|
-
moves: this.multipleSamplesNoReplace(pool, 4),
|
|
99
|
-
gender: false,
|
|
100
|
-
ability: 'No Ability',
|
|
101
|
-
evs: evs,
|
|
102
|
-
ivs: ivs,
|
|
103
|
-
item: '',
|
|
104
|
-
level,
|
|
105
|
-
happiness: 0,
|
|
106
|
-
shiny: false,
|
|
107
|
-
nature: 'Serious',
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return team;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Random team generation for Gen 1 Random Battles.
|
|
115
|
-
randomTeam() {
|
|
116
|
-
this.enforceNoDirectCustomBanlistChanges();
|
|
117
|
-
|
|
118
|
-
// Get what we need ready.
|
|
119
|
-
const seed = this.prng.seed;
|
|
120
|
-
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
121
|
-
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
|
122
|
-
|
|
123
|
-
// For Monotype
|
|
124
|
-
const isMonotype = !!this.forceMonotype || ruleTable.has('sametypeclause');
|
|
125
|
-
const typePool = this.dex.types.names();
|
|
126
|
-
const type = this.forceMonotype || this.sample(typePool);
|
|
127
|
-
|
|
128
|
-
/** Pokémon that are not wholly incompatible with the team, but still pretty bad */
|
|
129
|
-
const rejectedButNotInvalidPool: string[] = [];
|
|
130
|
-
const nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU'];
|
|
131
|
-
const uuTiers = ['NFE', 'UU', 'UUBL', 'NU'];
|
|
132
|
-
|
|
133
|
-
// Now let's store what we are getting.
|
|
134
|
-
const typeCount: {[k: string]: number} = {};
|
|
135
|
-
const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0};
|
|
136
|
-
let uberCount = 0;
|
|
137
|
-
let nuCount = 0;
|
|
138
|
-
|
|
139
|
-
const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
|
|
140
|
-
while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
|
|
141
|
-
const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
|
|
142
|
-
if (!species.exists || !species.randomBattleMoves) continue;
|
|
143
|
-
// Only one Ditto is allowed per battle in Generation 1,
|
|
144
|
-
// as it can cause an endless battle if two Dittos are forced
|
|
145
|
-
// to face each other.
|
|
146
|
-
if (species.id === 'ditto' && this.battleHasDitto) continue;
|
|
147
|
-
|
|
148
|
-
// Dynamically scale limits for different team sizes. The default and minimum value is 1.
|
|
149
|
-
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
|
|
150
|
-
|
|
151
|
-
const tier = species.tier;
|
|
152
|
-
switch (tier) {
|
|
153
|
-
case 'LC':
|
|
154
|
-
case 'NFE':
|
|
155
|
-
// Don't add pre-evo mon if already 4 or more non-OUs
|
|
156
|
-
// Regardless, pre-evo mons are slightly less common.
|
|
157
|
-
if (nuCount >= 4 * limitFactor || this.randomChance(1, 3)) continue;
|
|
158
|
-
break;
|
|
159
|
-
case 'Uber':
|
|
160
|
-
// Only allow a single Uber.
|
|
161
|
-
if (uberCount >= 1 * limitFactor) continue;
|
|
162
|
-
break;
|
|
163
|
-
default:
|
|
164
|
-
// OUs are fine. Otherwise 50% chance to skip mon if already 4 or more non-OUs.
|
|
165
|
-
if (uuTiers.includes(tier) && pokemonPool.length > 1 && (nuCount >= 4 * limitFactor && this.randomChance(1, 2))) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
let skip = false;
|
|
171
|
-
|
|
172
|
-
if (!isMonotype && !this.forceMonotype) {
|
|
173
|
-
// Limit 2 of any type as well. Diversity and minor weakness count.
|
|
174
|
-
// The second of a same type has halved chance of being added.
|
|
175
|
-
for (const typeName of species.types) {
|
|
176
|
-
if (typeCount[typeName] >= 2 * limitFactor ||
|
|
177
|
-
(typeCount[typeName] >= 1 * limitFactor && this.randomChance(1, 2) && pokemonPool.length > 1)) {
|
|
178
|
-
skip = true;
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (skip) {
|
|
184
|
-
rejectedButNotInvalidPool.push(species.id);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// We need a weakness count of spammable attacks to avoid being swept by those.
|
|
190
|
-
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake.
|
|
191
|
-
const pokemonWeaknesses = [];
|
|
192
|
-
for (const typeName in weaknessCount) {
|
|
193
|
-
const increaseCount = this.dex.getImmunity(typeName, species) && this.dex.getEffectiveness(typeName, species) > 0;
|
|
194
|
-
if (!increaseCount) continue;
|
|
195
|
-
if (weaknessCount[typeName] >= 2 * limitFactor) {
|
|
196
|
-
skip = true;
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
pokemonWeaknesses.push(typeName);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (skip) {
|
|
203
|
-
rejectedButNotInvalidPool.push(species.id);
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// The set passes the limitations.
|
|
208
|
-
pokemon.push(this.randomSet(species));
|
|
209
|
-
|
|
210
|
-
// Now let's increase the counters.
|
|
211
|
-
// Type counter.
|
|
212
|
-
for (const typeName of species.types) {
|
|
213
|
-
if (typeCount[typeName]) {
|
|
214
|
-
typeCount[typeName]++;
|
|
215
|
-
} else {
|
|
216
|
-
typeCount[typeName] = 1;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Weakness counter.
|
|
221
|
-
for (const weakness of pokemonWeaknesses) {
|
|
222
|
-
weaknessCount[weakness]++;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Increment tier bias counters.
|
|
226
|
-
if (tier === 'Uber') {
|
|
227
|
-
uberCount++;
|
|
228
|
-
} else if (nuTiers.includes(tier)) {
|
|
229
|
-
nuCount++;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Ditto check
|
|
233
|
-
if (species.id === 'ditto') this.battleHasDitto = true;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// if we don't have enough Pokémon, go back to rejects, which are already known to not be invalid.
|
|
237
|
-
while (pokemon.length < this.maxTeamSize && rejectedButNotInvalidPool.length) {
|
|
238
|
-
const species = this.sampleNoReplace(rejectedButNotInvalidPool);
|
|
239
|
-
pokemon.push(this.randomSet(species));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (pokemon.length < this.maxTeamSize && pokemon.length < 12 && !isMonotype) {
|
|
243
|
-
throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return pokemon;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
shouldCullMove(move: Move, types: Set<string>, moves: Set<string>, counter: MoveCounter): {cull: boolean} {
|
|
250
|
-
switch (move.id) {
|
|
251
|
-
// bit redundant to have both, but neither particularly better than the other
|
|
252
|
-
case 'hydropump':
|
|
253
|
-
return {cull: moves.has('surf')};
|
|
254
|
-
case 'surf':
|
|
255
|
-
return {cull: moves.has('hydropump')};
|
|
256
|
-
|
|
257
|
-
// other redundancies that aren't handled within the movesets themselves
|
|
258
|
-
case 'selfdestruct':
|
|
259
|
-
return {cull: moves.has('rest')};
|
|
260
|
-
case 'rest':
|
|
261
|
-
return {cull: moves.has('selfdestruct')};
|
|
262
|
-
case 'sharpen': case 'swordsdance':
|
|
263
|
-
return {cull: counter.get('Special') > counter.get('Physical') || !counter.get('Physical') || moves.has('growth')};
|
|
264
|
-
case 'growth':
|
|
265
|
-
return {cull: counter.get('Special') < counter.get('Physical') || !counter.get('Special') || moves.has('swordsdance')};
|
|
266
|
-
case 'poisonpowder': case 'stunspore': case 'sleeppowder': case 'toxic':
|
|
267
|
-
return {cull: counter.get('Status') > 1};
|
|
268
|
-
}
|
|
269
|
-
return {cull: false};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Random set generation for Gen 1 Random Battles.
|
|
274
|
-
*/
|
|
275
|
-
randomSet(species: string | Species): RandomTeamsTypes.RandomSet {
|
|
276
|
-
species = this.dex.species.get(species);
|
|
277
|
-
if (!species.exists) species = this.dex.species.get('pikachu'); // Because Gen 1.
|
|
278
|
-
|
|
279
|
-
const movePool = species.randomBattleMoves ? species.randomBattleMoves.slice() : [];
|
|
280
|
-
const moves = new Set<string>();
|
|
281
|
-
const types = new Set(species.types);
|
|
282
|
-
|
|
283
|
-
const counter = new MoveCounter();
|
|
284
|
-
|
|
285
|
-
// Moves that boost Attack:
|
|
286
|
-
const PhysicalSetup = ['swordsdance', 'sharpen'];
|
|
287
|
-
// Moves which boost Special Attack:
|
|
288
|
-
const SpecialSetup = ['amnesia', 'growth'];
|
|
289
|
-
|
|
290
|
-
// Either add all moves or add none
|
|
291
|
-
if (species.comboMoves && species.comboMoves.length <= this.maxMoveCount && this.randomChance(1, 2)) {
|
|
292
|
-
for (const m of species.comboMoves) moves.add(m);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Add one of the semi-mandatory moves
|
|
296
|
-
// Often, these are used so that the Pokemon only gets one of the less useful moves
|
|
297
|
-
if (moves.size < this.maxMoveCount && species.exclusiveMoves) {
|
|
298
|
-
moves.add(this.sample(species.exclusiveMoves));
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Add the mandatory move. SD Mew and Amnesia Snorlax are exceptions.
|
|
302
|
-
if (moves.size < this.maxMoveCount && species.essentialMove) {
|
|
303
|
-
moves.add(species.essentialMove);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
while (moves.size < this.maxMoveCount && movePool.length) {
|
|
307
|
-
// Choose next 4 moves from learnset/viable moves and add them to moves list:
|
|
308
|
-
while (moves.size < this.maxMoveCount && movePool.length) {
|
|
309
|
-
const moveid = this.sampleNoReplace(movePool);
|
|
310
|
-
moves.add(moveid);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Only do move choosing if we have backup moves in the pool...
|
|
314
|
-
if (movePool.length) {
|
|
315
|
-
for (const setMoveid of moves) {
|
|
316
|
-
const move = this.dex.moves.get(setMoveid);
|
|
317
|
-
const moveid = move.id;
|
|
318
|
-
if (!move.damage && !move.damageCallback) counter.add(move.category);
|
|
319
|
-
if (PhysicalSetup.includes(moveid)) counter.add('physicalsetup');
|
|
320
|
-
if (SpecialSetup.includes(moveid)) counter.add('specialsetup');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
for (const moveid of moves) {
|
|
324
|
-
if (moveid === species.essentialMove) continue;
|
|
325
|
-
const move = this.dex.moves.get(moveid);
|
|
326
|
-
if (
|
|
327
|
-
(!species.essentialMove || moveid !== species.essentialMove) &&
|
|
328
|
-
this.shouldCullMove(move, types, moves, counter).cull
|
|
329
|
-
) {
|
|
330
|
-
moves.delete(moveid);
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
counter.add(move.category);
|
|
334
|
-
}
|
|
335
|
-
} // End of the check for more than 4 moves on moveset.
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const levelScale: {[k: string]: number} = {
|
|
339
|
-
LC: 88,
|
|
340
|
-
NFE: 80,
|
|
341
|
-
PU: 77,
|
|
342
|
-
NU: 77,
|
|
343
|
-
NUBL: 76,
|
|
344
|
-
UU: 74,
|
|
345
|
-
UUBL: 71,
|
|
346
|
-
OU: 68,
|
|
347
|
-
Uber: 65,
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const customScale: {[k: string]: number} = {
|
|
351
|
-
Mewtwo: 62,
|
|
352
|
-
Caterpie: 100, Metapod: 100, Weedle: 100, Kakuna: 100, Magikarp: 100,
|
|
353
|
-
Ditto: 88,
|
|
354
|
-
};
|
|
355
|
-
const level = this.adjustLevel || customScale[species.name] || levelScale[species.tier] || 80;
|
|
356
|
-
|
|
357
|
-
return {
|
|
358
|
-
name: species.name,
|
|
359
|
-
species: species.name,
|
|
360
|
-
moves: Array.from(moves),
|
|
361
|
-
ability: 'No Ability',
|
|
362
|
-
evs: {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255},
|
|
363
|
-
ivs: {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30},
|
|
364
|
-
item: '',
|
|
365
|
-
level,
|
|
366
|
-
shiny: false,
|
|
367
|
-
gender: false,
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
randomHCTeam(): PokemonSet[] {
|
|
372
|
-
this.enforceNoDirectCustomBanlistChanges();
|
|
373
|
-
|
|
374
|
-
const team = [];
|
|
375
|
-
|
|
376
|
-
const movePool = [...this.dex.moves.all()];
|
|
377
|
-
const typesPool = ['Bird', ...this.dex.types.names()];
|
|
378
|
-
|
|
379
|
-
const randomN = this.randomNPokemon(this.maxTeamSize);
|
|
380
|
-
const hackmonsCup: {[k: string]: HackmonsCupEntry} = {};
|
|
381
|
-
|
|
382
|
-
for (const forme of randomN) {
|
|
383
|
-
// Choose forme
|
|
384
|
-
const species = this.dex.species.get(forme);
|
|
385
|
-
if (!hackmonsCup[species.id]) {
|
|
386
|
-
hackmonsCup[species.id] = {
|
|
387
|
-
types: [this.sample(typesPool), this.sample(typesPool)],
|
|
388
|
-
baseStats: {
|
|
389
|
-
hp: Utils.clampIntRange(this.random(256), 1),
|
|
390
|
-
atk: Utils.clampIntRange(this.random(256), 1),
|
|
391
|
-
def: Utils.clampIntRange(this.random(256), 1),
|
|
392
|
-
spa: Utils.clampIntRange(this.random(256), 1),
|
|
393
|
-
spd: 0,
|
|
394
|
-
spe: Utils.clampIntRange(this.random(256), 1),
|
|
395
|
-
},
|
|
396
|
-
};
|
|
397
|
-
if (this.forceMonotype && !hackmonsCup[species.id].types.includes(this.forceMonotype)) {
|
|
398
|
-
hackmonsCup[species.id].types[1] = this.forceMonotype;
|
|
399
|
-
}
|
|
400
|
-
hackmonsCup[species.id].baseStats.spd = hackmonsCup[species.id].baseStats.spa;
|
|
401
|
-
}
|
|
402
|
-
if (hackmonsCup[species.id].types[0] === hackmonsCup[species.id].types[1]) {
|
|
403
|
-
hackmonsCup[species.id].types.splice(1, 1);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Random unique moves
|
|
407
|
-
const moves = [];
|
|
408
|
-
do {
|
|
409
|
-
const move = this.sampleNoReplace(movePool);
|
|
410
|
-
if (move.gen <= this.gen && !move.isNonstandard && !move.name.startsWith('Hidden Power ')) {
|
|
411
|
-
moves.push(move.id);
|
|
412
|
-
}
|
|
413
|
-
} while (moves.length < this.maxMoveCount);
|
|
414
|
-
|
|
415
|
-
// Random EVs
|
|
416
|
-
const evs = {
|
|
417
|
-
hp: this.random(256),
|
|
418
|
-
atk: this.random(256),
|
|
419
|
-
def: this.random(256),
|
|
420
|
-
spa: this.random(256),
|
|
421
|
-
spd: 0,
|
|
422
|
-
spe: this.random(256),
|
|
423
|
-
};
|
|
424
|
-
evs['spd'] = evs['spa'];
|
|
425
|
-
|
|
426
|
-
// Random DVs
|
|
427
|
-
const ivs: StatsTable = {
|
|
428
|
-
hp: 0,
|
|
429
|
-
atk: this.random(16),
|
|
430
|
-
def: this.random(16),
|
|
431
|
-
spa: this.random(16),
|
|
432
|
-
spd: 0,
|
|
433
|
-
spe: this.random(16),
|
|
434
|
-
};
|
|
435
|
-
ivs["hp"] = (ivs["atk"] % 2) * 16 + (ivs["def"] % 2) * 8 + (ivs["spe"] % 2) * 4 + (ivs["spa"] % 2) * 2;
|
|
436
|
-
for (const iv in ivs) {
|
|
437
|
-
if (iv === 'hp' || iv === 'spd') continue;
|
|
438
|
-
ivs[iv as keyof StatsTable] *= 2;
|
|
439
|
-
}
|
|
440
|
-
ivs['spd'] = ivs['spa'];
|
|
441
|
-
|
|
442
|
-
// Level balance
|
|
443
|
-
const mbstmin = 425;
|
|
444
|
-
const baseStats = hackmonsCup[species.id].baseStats;
|
|
445
|
-
const calcStat = (statName: StatID, lvl?: number) => {
|
|
446
|
-
if (lvl) {
|
|
447
|
-
return Math.floor(Math.floor(2 * baseStats[statName] + ivs[statName] + Math.floor(evs[statName] / 4)) * lvl / 100 + 5);
|
|
448
|
-
}
|
|
449
|
-
return Math.floor(2 * baseStats[statName] + ivs[statName] + Math.floor(evs[statName] / 4)) + 5;
|
|
450
|
-
};
|
|
451
|
-
let mbst = 0;
|
|
452
|
-
for (const statName of Object.keys(baseStats)) {
|
|
453
|
-
mbst += calcStat(statName as StatID);
|
|
454
|
-
if (statName === 'hp') mbst += 5;
|
|
455
|
-
}
|
|
456
|
-
let level;
|
|
457
|
-
if (this.adjustLevel) {
|
|
458
|
-
level = this.adjustLevel;
|
|
459
|
-
} else {
|
|
460
|
-
level = Math.floor(100 * mbstmin / mbst);
|
|
461
|
-
while (level < 100) {
|
|
462
|
-
for (const statName of Object.keys(baseStats)) {
|
|
463
|
-
mbst += calcStat(statName as StatID, level);
|
|
464
|
-
if (statName === 'hp') mbst += 5;
|
|
465
|
-
}
|
|
466
|
-
if (mbst >= mbstmin) break;
|
|
467
|
-
level++;
|
|
468
|
-
}
|
|
469
|
-
if (level > 100) level = 100;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
team.push({
|
|
473
|
-
name: species.baseSpecies,
|
|
474
|
-
species: species.name,
|
|
475
|
-
gender: species.gender,
|
|
476
|
-
item: '',
|
|
477
|
-
ability: 'No Ability',
|
|
478
|
-
moves,
|
|
479
|
-
evs,
|
|
480
|
-
ivs,
|
|
481
|
-
nature: '',
|
|
482
|
-
level,
|
|
483
|
-
shiny: false,
|
|
484
|
-
// Hacky but the only way to communicate stats/level generation properly
|
|
485
|
-
hc: hackmonsCup[species.id],
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return team;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export default RandomGen1Teams;
|