@pkmn/dex 0.6.4 → 0.7.1
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/README.md +8 -8
- package/build/chunk-5VRACIDE.mjs +10 -0
- package/build/chunk-5VRACIDE.mjs.map +1 -0
- package/build/chunk-JC4IRQUL.js +10 -0
- package/build/chunk-JC4IRQUL.js.map +1 -0
- package/build/index.d.ts +23 -20
- package/build/index.js +71921 -1139
- package/build/index.js.map +1 -1
- package/build/index.min.js +1 -0
- package/build/index.mjs +71970 -0
- package/build/index.mjs.map +1 -0
- package/build/learnsets-C5W7H6N3.mjs +148380 -0
- package/build/learnsets-C5W7H6N3.mjs.map +1 -0
- package/build/learnsets-UXTEVWMG.js +148380 -0
- package/build/learnsets-UXTEVWMG.js.map +1 -0
- package/build/learnsets.min.js +1 -1
- package/package.json +9 -10
- package/build/data/abilities.json +0 -2528
- package/build/data/aliases.json +0 -1803
- package/build/data/conditions.json +0 -200
- package/build/data/formats-data.json +0 -5267
- package/build/data/items.json +0 -5274
- package/build/data/learnsets.json +0 -130290
- package/build/data/moves.json +0 -18514
- package/build/data/natures.json +0 -29
- package/build/data/species.json +0 -27354
- package/build/data/types.json +0 -737
- package/build/production.min.js +0 -1
- package/index.ts +0 -1489
package/index.ts
DELETED
|
@@ -1,1489 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
2
|
-
import * as T from '@pkmn/dex-types';
|
|
3
|
-
|
|
4
|
-
import * as AbilitiesJSON from './data/abilities.json';
|
|
5
|
-
import * as AliasesJSON from './data/aliases.json';
|
|
6
|
-
import * as ConditionsJSON from './data/conditions.json';
|
|
7
|
-
import * as ItemsJSON from './data/items.json';
|
|
8
|
-
import * as MovesJSON from './data/moves.json';
|
|
9
|
-
import * as NaturesJSON from './data/natures.json';
|
|
10
|
-
import * as SpeciesJSON from './data/species.json';
|
|
11
|
-
import * as TypesJSON from './data/types.json';
|
|
12
|
-
import * as FormatsDataJSON from './data/formats-data.json';
|
|
13
|
-
|
|
14
|
-
export function toID(text: any): T.ID {
|
|
15
|
-
if (text?.id) text = text.id;
|
|
16
|
-
if (typeof text !== 'string' && typeof text !== 'number') return '';
|
|
17
|
-
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '') as T.ID;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getString(str: any): string {
|
|
21
|
-
return (typeof str === 'string' || typeof str === 'number') ? '' + str : '';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// #region Data
|
|
25
|
-
|
|
26
|
-
export interface FormatData {
|
|
27
|
-
tier?: string;
|
|
28
|
-
doublesTier?: string;
|
|
29
|
-
isNonstandard?: T.Nonstandard;
|
|
30
|
-
inherit?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface AnyObject { [k: string]: any }
|
|
34
|
-
|
|
35
|
-
export class BasicEffect<NameT extends string = string> implements T.BasicEffect<NameT> {
|
|
36
|
-
id: T.ID;
|
|
37
|
-
name: NameT;
|
|
38
|
-
fullname: string;
|
|
39
|
-
effectType: T.EffectType;
|
|
40
|
-
kind: T.DataKind;
|
|
41
|
-
exists: boolean;
|
|
42
|
-
num: number;
|
|
43
|
-
gen: T.GenerationNum;
|
|
44
|
-
shortDesc: string;
|
|
45
|
-
desc: string;
|
|
46
|
-
isNonstandard: T.Nonstandard | null;
|
|
47
|
-
duration?: number;
|
|
48
|
-
|
|
49
|
-
constructor(data: AnyObject) {
|
|
50
|
-
this.exists = true;
|
|
51
|
-
Object.assign(this, data);
|
|
52
|
-
|
|
53
|
-
this.name = getString(data.name).trim() as NameT;
|
|
54
|
-
this.id = data.realMove ? toID(data.realMove) : toID(this.name); // Hidden Power hack
|
|
55
|
-
this.fullname = getString(data.fullname) || this.name;
|
|
56
|
-
this.effectType = getString(data.effectType) as T.EffectType || 'Condition';
|
|
57
|
-
this.kind = 'Condition';
|
|
58
|
-
this.exists = !!(this.exists && this.id);
|
|
59
|
-
this.num = data.num || 0;
|
|
60
|
-
this.gen = data.gen || 0;
|
|
61
|
-
this.shortDesc = data.shortDesc || '';
|
|
62
|
-
this.desc = data.desc || this.shortDesc;
|
|
63
|
-
this.isNonstandard = data.isNonstandard || null;
|
|
64
|
-
this.duration = data.duration;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
toString() {
|
|
68
|
-
return this.name;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export class Condition extends BasicEffect implements T.Condition {
|
|
73
|
-
readonly effectType: 'Condition' | 'Weather' | 'Status';
|
|
74
|
-
readonly kind: 'Condition';
|
|
75
|
-
|
|
76
|
-
constructor(data: AnyObject) {
|
|
77
|
-
super(data);
|
|
78
|
-
data = this;
|
|
79
|
-
this.effectType =
|
|
80
|
-
(['Weather', 'Status'].includes(data.effectType) ? data.effectType : 'Condition');
|
|
81
|
-
this.kind = 'Condition';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const EMPTY_CONDITION: Condition = new Condition({name: '', exists: false});
|
|
86
|
-
|
|
87
|
-
class DexConditions implements T.DexTable<Condition> {
|
|
88
|
-
readonly dex: ModdedDex;
|
|
89
|
-
readonly cache = Object.create(null) as { [id: string]: Condition };
|
|
90
|
-
|
|
91
|
-
constructor(dex: ModdedDex) {
|
|
92
|
-
this.dex = dex;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
get(name: string): Condition {
|
|
96
|
-
// This treatment of 'item:id' and 'ability:id' is kind of repugnant but regrettably exists
|
|
97
|
-
// for loose compatibility with Pokémon Showdown's Dex API. 'move:id' also needs to be handled
|
|
98
|
-
// for the client to work correctly.
|
|
99
|
-
if (name.startsWith('item:')) {
|
|
100
|
-
const item = this.dex.items.get(name.slice(5));
|
|
101
|
-
return item as any as Condition;
|
|
102
|
-
} else if (name.startsWith('ability:')) {
|
|
103
|
-
const ability = this.dex.abilities.get(name.slice(8));
|
|
104
|
-
return ability as any as Condition;
|
|
105
|
-
} else if (name.startsWith('move:')) {
|
|
106
|
-
const move = this.dex.moves.get(name.slice(5));
|
|
107
|
-
return move as any as Condition;
|
|
108
|
-
}
|
|
109
|
-
return this.getByID(toID(name));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
getByID(id: T.ID): Condition {
|
|
113
|
-
if (!id) return EMPTY_CONDITION;
|
|
114
|
-
|
|
115
|
-
let condition = this.cache[id];
|
|
116
|
-
if (condition) return condition;
|
|
117
|
-
|
|
118
|
-
let found: T.AbilityData | T.ItemData | T.MoveData;
|
|
119
|
-
if (this.dex.data.Conditions.hasOwnProperty(id)) {
|
|
120
|
-
condition = new Condition({kind: 'Condition', ...this.dex.data.Conditions[id]});
|
|
121
|
-
} else if (
|
|
122
|
-
(this.dex.data.Moves.hasOwnProperty(id) &&
|
|
123
|
-
(found = this.dex.data.Moves[id]).condition) ||
|
|
124
|
-
(this.dex.data.Abilities.hasOwnProperty(id) &&
|
|
125
|
-
(found = this.dex.data.Abilities[id]).condition) ||
|
|
126
|
-
(this.dex.data.Items.hasOwnProperty(id) &&
|
|
127
|
-
(found = this.dex.data.Items[id]).condition)) {
|
|
128
|
-
condition = new Condition({name: found.name || id, ...found.condition});
|
|
129
|
-
} else if (id === 'recoil') {
|
|
130
|
-
condition = new Condition({name: 'Recoil', effectType: 'Recoil'});
|
|
131
|
-
} else if (id === 'drain') {
|
|
132
|
-
condition = new Condition({name: 'Drain', effectType: 'Drain'});
|
|
133
|
-
} else {
|
|
134
|
-
condition = new Condition({name: id, exists: false});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.cache[id] = condition;
|
|
138
|
-
return condition;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export class Ability extends BasicEffect<T.AbilityName> implements T.Ability {
|
|
143
|
-
readonly effectType: 'Ability';
|
|
144
|
-
readonly kind: 'Ability';
|
|
145
|
-
readonly isBreakable?: boolean;
|
|
146
|
-
readonly suppressWeather?: boolean;
|
|
147
|
-
readonly condition?: Partial<Condition>;
|
|
148
|
-
|
|
149
|
-
constructor(data: AnyObject) {
|
|
150
|
-
super(data);
|
|
151
|
-
data = this;
|
|
152
|
-
|
|
153
|
-
this.fullname = `ability: ${this.name}`;
|
|
154
|
-
this.effectType = 'Ability';
|
|
155
|
-
this.kind = 'Ability';
|
|
156
|
-
|
|
157
|
-
if (!this.gen) {
|
|
158
|
-
if (this.num >= 234) {
|
|
159
|
-
this.gen = 8;
|
|
160
|
-
} else if (this.num >= 192) {
|
|
161
|
-
this.gen = 7;
|
|
162
|
-
} else if (this.num >= 165) {
|
|
163
|
-
this.gen = 6;
|
|
164
|
-
} else if (this.num >= 124) {
|
|
165
|
-
this.gen = 5;
|
|
166
|
-
} else if (this.num >= 77) {
|
|
167
|
-
this.gen = 4;
|
|
168
|
-
} else if (this.num >= 1) {
|
|
169
|
-
this.gen = 3;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
class DexAbilities implements T.DexTable<Ability> {
|
|
176
|
-
readonly dex: ModdedDex;
|
|
177
|
-
readonly caches = {
|
|
178
|
-
get: Object.create(null) as { [id: string]: Ability },
|
|
179
|
-
all: undefined as ReadonlyArray<Ability> | undefined,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
constructor(dex: ModdedDex) {
|
|
183
|
-
this.dex = dex;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
get(name: string): Ability {
|
|
187
|
-
return this.getByID(toID(name));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
getByID(id: T.ID): Ability {
|
|
191
|
-
const alias = this.dex.data.Aliases[id];
|
|
192
|
-
if (alias) id = toID(alias);
|
|
193
|
-
|
|
194
|
-
let ability = this.caches.get[id];
|
|
195
|
-
if (ability) return ability;
|
|
196
|
-
|
|
197
|
-
const data = this.dex.data.Abilities[id];
|
|
198
|
-
if (id && data) {
|
|
199
|
-
ability = new Ability(data);
|
|
200
|
-
if (ability.gen > this.dex.gen) (ability as any).isNonstandard = 'Future';
|
|
201
|
-
if (this.dex.gen <= 2 && ability.id === 'noability') (ability as any).isNonstandard = null;
|
|
202
|
-
} else {
|
|
203
|
-
ability = new Ability({id, name: id, exists: false});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (ability.exists) this.caches.get[id] = ability;
|
|
207
|
-
return ability;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
all(): readonly Ability[] {
|
|
211
|
-
if (this.caches.all) return this.caches.all;
|
|
212
|
-
const abilities = [];
|
|
213
|
-
for (const id in this.dex.data.Abilities) {
|
|
214
|
-
abilities.push(this.getByID(id as T.ID));
|
|
215
|
-
}
|
|
216
|
-
return (this.caches.all = abilities);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export class Item extends BasicEffect<T.ItemName> implements T.Item {
|
|
221
|
-
readonly effectType: 'Item';
|
|
222
|
-
readonly kind: 'Item';
|
|
223
|
-
|
|
224
|
-
readonly forcedForme?: T.SpeciesName;
|
|
225
|
-
readonly megaStone?: T.SpeciesName;
|
|
226
|
-
readonly megaEvolves?: T.SpeciesName;
|
|
227
|
-
readonly onDrive?: T.TypeName;
|
|
228
|
-
readonly onMemory?: T.TypeName;
|
|
229
|
-
readonly onPlate?: T.TypeName;
|
|
230
|
-
readonly zMove?: T.MoveName | true;
|
|
231
|
-
readonly zMoveType?: T.TypeName;
|
|
232
|
-
readonly zMoveFrom?: T.MoveName;
|
|
233
|
-
readonly itemUser?: T.SpeciesName[];
|
|
234
|
-
readonly fling?: T.ItemData['fling'];
|
|
235
|
-
readonly condition?: Partial<Condition>;
|
|
236
|
-
readonly ignoreKlutz?: boolean;
|
|
237
|
-
readonly isBerry?: boolean;
|
|
238
|
-
readonly isChoice?: boolean;
|
|
239
|
-
readonly isGem?: boolean;
|
|
240
|
-
readonly isPokeball?: boolean;
|
|
241
|
-
readonly naturalGift?: { basePower: number; type: T.TypeName };
|
|
242
|
-
readonly boosts?: Partial<T.BoostsTable> | false;
|
|
243
|
-
|
|
244
|
-
constructor(data: AnyObject) {
|
|
245
|
-
super(data);
|
|
246
|
-
data = this;
|
|
247
|
-
|
|
248
|
-
this.fullname = `item: ${this.name}`;
|
|
249
|
-
this.effectType = 'Item';
|
|
250
|
-
this.kind = 'Item';
|
|
251
|
-
|
|
252
|
-
if (!this.gen) {
|
|
253
|
-
if (this.num >= 689) {
|
|
254
|
-
this.gen = 7;
|
|
255
|
-
} else if (this.num >= 577) {
|
|
256
|
-
this.gen = 6;
|
|
257
|
-
} else if (this.num >= 537) {
|
|
258
|
-
this.gen = 5;
|
|
259
|
-
} else if (this.num >= 377) {
|
|
260
|
-
this.gen = 4;
|
|
261
|
-
} else {
|
|
262
|
-
this.gen = 3;
|
|
263
|
-
}
|
|
264
|
-
// Due to difference in gen 2 item numbering, gen 2 items must be
|
|
265
|
-
// specified manually
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (this.isBerry) this.fling = {basePower: 10};
|
|
269
|
-
if (this.id.endsWith('plate')) this.fling = {basePower: 90};
|
|
270
|
-
if (this.onDrive) this.fling = {basePower: 70};
|
|
271
|
-
if (this.megaStone) this.fling = {basePower: 80};
|
|
272
|
-
if (this.onMemory) this.fling = {basePower: 50};
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
class DexItems implements T.DexTable<Item> {
|
|
277
|
-
readonly dex: ModdedDex;
|
|
278
|
-
readonly caches = {
|
|
279
|
-
get: Object.create(null) as { [id: string]: Item },
|
|
280
|
-
all: undefined as ReadonlyArray<Item> | undefined,
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
constructor(dex: ModdedDex) {
|
|
284
|
-
this.dex = dex;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
get(name?: string): Item {
|
|
288
|
-
return this.getByID(toID(name));
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
getByID(id: T.ID): Item {
|
|
292
|
-
const alias = this.dex.data.Aliases[id];
|
|
293
|
-
if (alias) id = toID(alias);
|
|
294
|
-
|
|
295
|
-
let item = this.caches.get[id];
|
|
296
|
-
if (item) return item;
|
|
297
|
-
|
|
298
|
-
if (id && !this.dex.data.Items[id] && this.dex.data.Items[id + 'berry']) {
|
|
299
|
-
item = this.getByID(id + 'berry' as T.ID);
|
|
300
|
-
return (this.caches.get[id] = item);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const data = this.dex.data.Items[id];
|
|
304
|
-
if (id && data) {
|
|
305
|
-
item = new Item(data);
|
|
306
|
-
if (item.gen > this.dex.gen) (item as any).isNonstandard = 'Future';
|
|
307
|
-
} else {
|
|
308
|
-
item = new Item({name: id, exists: false});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (item.exists) this.caches.get[id] = item;
|
|
312
|
-
return item;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
all(): readonly Item[] {
|
|
316
|
-
if (this.caches.all) return this.caches.all;
|
|
317
|
-
const items = [];
|
|
318
|
-
for (const id in this.dex.data.Items) {
|
|
319
|
-
items.push(this.getByID(id as T.ID));
|
|
320
|
-
}
|
|
321
|
-
return (this.caches.all = items);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export class Move extends BasicEffect<T.MoveName> implements T.Move {
|
|
326
|
-
readonly effectType: 'Move';
|
|
327
|
-
readonly kind: 'Move';
|
|
328
|
-
|
|
329
|
-
readonly boosts?: Partial<T.BoostsTable>;
|
|
330
|
-
readonly status?: T.StatusName;
|
|
331
|
-
readonly volatileStatus?: T.ID;
|
|
332
|
-
readonly slotCondition?: T.ID;
|
|
333
|
-
readonly sideCondition?: T.ID;
|
|
334
|
-
readonly terrain?: T.ID;
|
|
335
|
-
readonly pseudoWeather?: T.ID;
|
|
336
|
-
readonly weather?: T.ID;
|
|
337
|
-
|
|
338
|
-
readonly basePower!: number;
|
|
339
|
-
readonly type!: T.TypeName;
|
|
340
|
-
readonly accuracy!: true | number;
|
|
341
|
-
readonly pp!: number;
|
|
342
|
-
readonly target!: T.MoveTarget;
|
|
343
|
-
readonly priority!: number;
|
|
344
|
-
readonly flags: T.Move['flags'];
|
|
345
|
-
readonly category!: T.MoveCategory;
|
|
346
|
-
|
|
347
|
-
readonly condition?: Partial<T.Condition>;
|
|
348
|
-
readonly damage?: number | 'level' | false | null;
|
|
349
|
-
readonly noPPBoosts?: boolean;
|
|
350
|
-
|
|
351
|
-
readonly isZ: boolean | T.ID;
|
|
352
|
-
readonly zMove?: {
|
|
353
|
-
basePower?: number;
|
|
354
|
-
effect?: T.ID;
|
|
355
|
-
boost?: Partial<T.BoostsTable>;
|
|
356
|
-
};
|
|
357
|
-
readonly isMax: boolean | T.SpeciesName;
|
|
358
|
-
readonly maxMove?: {
|
|
359
|
-
basePower: number;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
readonly ohko?: boolean | T.TypeName;
|
|
363
|
-
readonly thawsTarget?: boolean;
|
|
364
|
-
readonly heal?: number[] | null;
|
|
365
|
-
readonly forceSwitch?: boolean;
|
|
366
|
-
readonly selfSwitch?: boolean | 'copyvolatile';
|
|
367
|
-
readonly selfBoost?: { boosts?: Partial<T.BoostsTable> };
|
|
368
|
-
readonly selfdestruct?: boolean | 'ifHit' | 'always';
|
|
369
|
-
readonly breaksProtect?: boolean;
|
|
370
|
-
readonly recoil?: [number, number];
|
|
371
|
-
readonly drain?: [number, number];
|
|
372
|
-
readonly mindBlownRecoil?: boolean;
|
|
373
|
-
readonly struggleRecoil?: boolean;
|
|
374
|
-
readonly stealsBoosts?: boolean;
|
|
375
|
-
readonly secondary?: T.SecondaryEffect | null;
|
|
376
|
-
readonly secondaries: T.SecondaryEffect[] | null;
|
|
377
|
-
readonly self?: T.HitEffect | null;
|
|
378
|
-
|
|
379
|
-
readonly alwaysHit?: boolean;
|
|
380
|
-
readonly basePowerModifier?: number;
|
|
381
|
-
readonly critModifier?: number;
|
|
382
|
-
readonly critRatio?: number;
|
|
383
|
-
readonly overrideOffensivePokemon?: 'target' | 'source';
|
|
384
|
-
readonly overrideOffensiveStat?: Exclude<T.StatID, 'hp'>;
|
|
385
|
-
readonly overrideDefensivePokemon?: 'target' | 'source';
|
|
386
|
-
readonly overrideDefensiveStat?: Exclude<T.StatID, 'hp'>;
|
|
387
|
-
readonly forceSTAB?: boolean;
|
|
388
|
-
readonly ignoreAbility?: boolean;
|
|
389
|
-
readonly ignoreAccuracy?: boolean;
|
|
390
|
-
readonly ignoreDefensive?: boolean;
|
|
391
|
-
readonly ignoreEvasion?: boolean;
|
|
392
|
-
readonly ignoreImmunity?: boolean | { [k in keyof T.TypeName]?: boolean };
|
|
393
|
-
readonly ignoreNegativeOffensive?: boolean;
|
|
394
|
-
readonly ignoreOffensive?: boolean;
|
|
395
|
-
readonly ignorePositiveDefensive?: boolean;
|
|
396
|
-
readonly ignorePositiveEvasion?: boolean;
|
|
397
|
-
readonly infiltrates?: boolean;
|
|
398
|
-
readonly multiaccuracy?: boolean;
|
|
399
|
-
readonly multihit?: number | number[];
|
|
400
|
-
readonly noCopy?: boolean;
|
|
401
|
-
readonly noDamageVariance?: boolean;
|
|
402
|
-
readonly noFaint?: boolean;
|
|
403
|
-
readonly nonGhostTarget?: T.MoveTarget;
|
|
404
|
-
readonly pressureTarget?: T.MoveTarget;
|
|
405
|
-
readonly sleepUsable?: boolean;
|
|
406
|
-
readonly smartTarget?: boolean;
|
|
407
|
-
readonly spreadModifier?: number;
|
|
408
|
-
readonly tracksTarget?: boolean;
|
|
409
|
-
readonly willCrit?: boolean;
|
|
410
|
-
|
|
411
|
-
readonly hasCrashDamage?: boolean;
|
|
412
|
-
readonly isConfusionSelfHit?: boolean;
|
|
413
|
-
readonly isFutureMove?: boolean;
|
|
414
|
-
readonly noMetronome?: T.MoveName[];
|
|
415
|
-
readonly noSketch?: boolean;
|
|
416
|
-
readonly stallingMove?: boolean;
|
|
417
|
-
|
|
418
|
-
constructor(data: AnyObject) {
|
|
419
|
-
super(data);
|
|
420
|
-
data = this;
|
|
421
|
-
|
|
422
|
-
this.fullname = `move: ${this.name}`;
|
|
423
|
-
this.effectType = 'Move';
|
|
424
|
-
this.kind = 'Move';
|
|
425
|
-
|
|
426
|
-
this.type = getString(data.type) as T.TypeName;
|
|
427
|
-
this.basePower = Number(data.basePower!);
|
|
428
|
-
this.critRatio = Number(data.critRatio) || 1;
|
|
429
|
-
this.secondary = data.secondary || null;
|
|
430
|
-
this.secondaries = data.secondaries?.length
|
|
431
|
-
? data.secondaries : this.secondary
|
|
432
|
-
? [this.secondary]
|
|
433
|
-
: null;
|
|
434
|
-
this.priority = Number(data.priority) || 0;
|
|
435
|
-
this.ignoreImmunity =
|
|
436
|
-
(data.ignoreImmunity !== undefined ? data.ignoreImmunity : data.category === 'Status');
|
|
437
|
-
this.pp = Number(data.pp!);
|
|
438
|
-
this.isZ = data.isZ || false;
|
|
439
|
-
this.isMax = data.isMax || false;
|
|
440
|
-
this.flags = data.flags || {};
|
|
441
|
-
this.selfSwitch =
|
|
442
|
-
(typeof data.selfSwitch === 'string'
|
|
443
|
-
? (data.selfSwitch as T.ID)
|
|
444
|
-
: data.selfSwitch) ||
|
|
445
|
-
undefined;
|
|
446
|
-
this.pressureTarget = data.pressureTarget || undefined;
|
|
447
|
-
this.nonGhostTarget = data.nonGhostTarget || undefined;
|
|
448
|
-
this.ignoreAbility = data.ignoreAbility || false;
|
|
449
|
-
this.volatileStatus =
|
|
450
|
-
typeof data.volatileStatus === 'string' ? (data.volatileStatus as T.ID) : undefined;
|
|
451
|
-
|
|
452
|
-
if (this.category !== 'Status' && !this.maxMove && this.id !== 'struggle') {
|
|
453
|
-
this.maxMove = {basePower: 1};
|
|
454
|
-
if (this.isMax || this.isZ) {
|
|
455
|
-
// already initialized to 1
|
|
456
|
-
} else if (!this.basePower) {
|
|
457
|
-
this.maxMove.basePower = 100;
|
|
458
|
-
} else if (['Fighting', 'Poison'].includes(this.type)) {
|
|
459
|
-
if (this.basePower >= 150) {
|
|
460
|
-
this.maxMove.basePower = 100;
|
|
461
|
-
} else if (this.basePower >= 110) {
|
|
462
|
-
this.maxMove.basePower = 95;
|
|
463
|
-
} else if (this.basePower >= 75) {
|
|
464
|
-
this.maxMove.basePower = 90;
|
|
465
|
-
} else if (this.basePower >= 65) {
|
|
466
|
-
this.maxMove.basePower = 85;
|
|
467
|
-
} else if (this.basePower >= 55) {
|
|
468
|
-
this.maxMove.basePower = 80;
|
|
469
|
-
} else if (this.basePower >= 45) {
|
|
470
|
-
this.maxMove.basePower = 75;
|
|
471
|
-
} else {
|
|
472
|
-
this.maxMove.basePower = 70;
|
|
473
|
-
}
|
|
474
|
-
} else {
|
|
475
|
-
if (this.basePower >= 150) {
|
|
476
|
-
this.maxMove.basePower = 150;
|
|
477
|
-
} else if (this.basePower >= 110) {
|
|
478
|
-
this.maxMove.basePower = 140;
|
|
479
|
-
} else if (this.basePower >= 75) {
|
|
480
|
-
this.maxMove.basePower = 130;
|
|
481
|
-
} else if (this.basePower >= 65) {
|
|
482
|
-
this.maxMove.basePower = 120;
|
|
483
|
-
} else if (this.basePower >= 55) {
|
|
484
|
-
this.maxMove.basePower = 110;
|
|
485
|
-
} else if (this.basePower >= 45) {
|
|
486
|
-
this.maxMove.basePower = 100;
|
|
487
|
-
} else {
|
|
488
|
-
this.maxMove.basePower = 90;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
if (this.category !== 'Status' && !this.zMove &&
|
|
493
|
-
!this.isZ && !this.isMax && this.id !== 'struggle') {
|
|
494
|
-
let basePower = this.basePower;
|
|
495
|
-
this.zMove = {};
|
|
496
|
-
if (Array.isArray(this.multihit)) basePower *= 3;
|
|
497
|
-
if (!basePower) {
|
|
498
|
-
this.zMove.basePower = 100;
|
|
499
|
-
} else if (basePower >= 140) {
|
|
500
|
-
this.zMove.basePower = 200;
|
|
501
|
-
} else if (basePower >= 130) {
|
|
502
|
-
this.zMove.basePower = 195;
|
|
503
|
-
} else if (basePower >= 120) {
|
|
504
|
-
this.zMove.basePower = 190;
|
|
505
|
-
} else if (basePower >= 110) {
|
|
506
|
-
this.zMove.basePower = 185;
|
|
507
|
-
} else if (basePower >= 100) {
|
|
508
|
-
this.zMove.basePower = 180;
|
|
509
|
-
} else if (basePower >= 90) {
|
|
510
|
-
this.zMove.basePower = 175;
|
|
511
|
-
} else if (basePower >= 80) {
|
|
512
|
-
this.zMove.basePower = 160;
|
|
513
|
-
} else if (basePower >= 70) {
|
|
514
|
-
this.zMove.basePower = 140;
|
|
515
|
-
} else if (basePower >= 60) {
|
|
516
|
-
this.zMove.basePower = 120;
|
|
517
|
-
} else {
|
|
518
|
-
this.zMove.basePower = 100;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
if (!this.gen) {
|
|
522
|
-
if (this.num >= 743) {
|
|
523
|
-
this.gen = 8;
|
|
524
|
-
} else if (this.num >= 622) {
|
|
525
|
-
this.gen = 7;
|
|
526
|
-
} else if (this.num >= 560) {
|
|
527
|
-
this.gen = 6;
|
|
528
|
-
} else if (this.num >= 468) {
|
|
529
|
-
this.gen = 5;
|
|
530
|
-
} else if (this.num >= 355) {
|
|
531
|
-
this.gen = 4;
|
|
532
|
-
} else if (this.num >= 252) {
|
|
533
|
-
this.gen = 3;
|
|
534
|
-
} else if (this.num >= 166) {
|
|
535
|
-
this.gen = 2;
|
|
536
|
-
} else if (this.num >= 1) {
|
|
537
|
-
this.gen = 1;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
class DexMoves implements T.DexTable<Move> {
|
|
544
|
-
readonly dex: ModdedDex;
|
|
545
|
-
readonly caches = {
|
|
546
|
-
get: Object.create(null) as { [id: string]: Move },
|
|
547
|
-
all: undefined as ReadonlyArray<Move> | undefined,
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
constructor(dex: ModdedDex) {
|
|
551
|
-
this.dex = dex;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
get(name: string): Move {
|
|
555
|
-
return this.getByID(toID(name));
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
getByID(id: T.ID): Move {
|
|
559
|
-
const alias = this.dex.data.Aliases[id];
|
|
560
|
-
if (alias) id = toID(alias);
|
|
561
|
-
|
|
562
|
-
let move = this.caches.get[id];
|
|
563
|
-
if (move) return move;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (id.startsWith('hiddenpower')) {
|
|
567
|
-
id = /([a-z]*)([0-9]*)/.exec(id)![1] as T.ID;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const data = this.dex.data.Moves[id];
|
|
571
|
-
if (id && data) {
|
|
572
|
-
move = new Move(data);
|
|
573
|
-
if (id.substr(0, 11) === 'hiddenpower') {
|
|
574
|
-
id = /([a-z]*)([0-9]*)/.exec(id)![1] as T.ID;
|
|
575
|
-
} else if (id.substr(0, 6) === 'return' && id.length > 6) {
|
|
576
|
-
id = 'return' as T.ID;
|
|
577
|
-
(move as any).basePower = Number(id.slice(6));
|
|
578
|
-
} else if (id.substr(0, 11) === 'frustration' && id.length > 11) {
|
|
579
|
-
id = 'frustration' as T.ID;
|
|
580
|
-
(move as any).basePower = Number(id.slice(11));
|
|
581
|
-
}
|
|
582
|
-
if (this.dex.gen <= 3 && data.category !== 'Status') {
|
|
583
|
-
(move as any).category = getGen3Category(data.type);
|
|
584
|
-
}
|
|
585
|
-
if (move.gen > this.dex.gen) (move as any).isNonstandard = 'Future';
|
|
586
|
-
} else {
|
|
587
|
-
move = new Move({id, name: id, exists: false});
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (move.exists) this.caches.get[id] = move;
|
|
591
|
-
return move;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
all(): readonly Move[] {
|
|
595
|
-
if (this.caches.all) return this.caches.all;
|
|
596
|
-
const moves = [];
|
|
597
|
-
for (const id in this.dex.data.Moves) {
|
|
598
|
-
moves.push(this.getByID(id as T.ID));
|
|
599
|
-
}
|
|
600
|
-
return (this.caches.all = moves);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
export class Nature extends BasicEffect<T.NatureName> implements T.Nature {
|
|
605
|
-
readonly effectType: 'Nature';
|
|
606
|
-
readonly kind: 'Nature';
|
|
607
|
-
readonly plus?: Exclude<T.StatID, 'hp'>;
|
|
608
|
-
readonly minus?: Exclude<T.StatID, 'hp'>;
|
|
609
|
-
constructor(data: AnyObject) {
|
|
610
|
-
super(data);
|
|
611
|
-
data = this;
|
|
612
|
-
|
|
613
|
-
this.fullname = `nature: ${this.name}`;
|
|
614
|
-
this.effectType = 'Nature';
|
|
615
|
-
this.kind = 'Nature';
|
|
616
|
-
this.gen = 3;
|
|
617
|
-
this.plus = data.plus || undefined;
|
|
618
|
-
this.minus = data.minus || undefined;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
class DexNatures implements T.DexTable<Nature> {
|
|
623
|
-
readonly dex: ModdedDex;
|
|
624
|
-
readonly caches = {
|
|
625
|
-
get: Object.create(null) as { [id: string]: Nature },
|
|
626
|
-
all: undefined as ReadonlyArray<Nature> | undefined,
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
constructor(dex: ModdedDex) {
|
|
630
|
-
this.dex = dex;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
get(name: string): Nature {
|
|
634
|
-
return this.getByID(toID(name));
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
getByID(id: T.ID): Nature {
|
|
638
|
-
const alias = this.dex.data.Aliases[id];
|
|
639
|
-
if (alias) id = toID(alias);
|
|
640
|
-
|
|
641
|
-
let nature = this.caches.get[id];
|
|
642
|
-
if (nature) return nature;
|
|
643
|
-
|
|
644
|
-
const data = this.dex.data.Natures[id];
|
|
645
|
-
if (id && data) {
|
|
646
|
-
nature = new Nature(data);
|
|
647
|
-
if (nature.gen > this.dex.gen) nature.isNonstandard = 'Future';
|
|
648
|
-
} else {
|
|
649
|
-
nature = new Nature({name: id, exists: false});
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
if (nature.exists) this.caches.get[id] = nature;
|
|
653
|
-
return nature;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
all(): readonly Nature[] {
|
|
657
|
-
if (this.caches.all) return this.caches.all;
|
|
658
|
-
const natures = [];
|
|
659
|
-
for (const id in this.dex.data.Natures) {
|
|
660
|
-
natures.push(this.getByID(id as T.ID));
|
|
661
|
-
}
|
|
662
|
-
return (this.caches.all = natures);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
export class Species extends BasicEffect<T.SpeciesName> implements T.Species {
|
|
667
|
-
readonly effectType: 'Pokemon';
|
|
668
|
-
readonly kind: 'Species';
|
|
669
|
-
|
|
670
|
-
readonly baseStats: T.StatsTable;
|
|
671
|
-
readonly bst: number;
|
|
672
|
-
readonly baseSpecies: T.SpeciesName;
|
|
673
|
-
readonly baseForme: T.FormeName | '';
|
|
674
|
-
readonly forme: T.FormeName | '';
|
|
675
|
-
readonly abilities: T.SpeciesAbility<T.AbilityName | ''>;
|
|
676
|
-
readonly types: [T.TypeName] | [T.TypeName, T.TypeName];
|
|
677
|
-
readonly prevo?: T.SpeciesName | '';
|
|
678
|
-
readonly evos?: T.SpeciesName[];
|
|
679
|
-
readonly nfe: boolean;
|
|
680
|
-
readonly eggGroups: T.EggGroup[];
|
|
681
|
-
readonly canHatch: boolean;
|
|
682
|
-
readonly weightkg: number;
|
|
683
|
-
readonly weighthg: number;
|
|
684
|
-
readonly tags: T.SpeciesTag[];
|
|
685
|
-
readonly unreleasedHidden: boolean | 'Past';
|
|
686
|
-
readonly maleOnlyHidden: boolean;
|
|
687
|
-
readonly changesFrom?: T.SpeciesName;
|
|
688
|
-
readonly tier: T.Tier.Singles | T.Tier.Other | 'Illegal';
|
|
689
|
-
readonly doublesTier: T.Tier.Doubles | 'Illegal';
|
|
690
|
-
|
|
691
|
-
readonly cosmeticFormes?: T.SpeciesName[];
|
|
692
|
-
readonly otherFormes?: T.SpeciesName[];
|
|
693
|
-
readonly genderRatio: { M: number; F: number };
|
|
694
|
-
readonly isMega?: boolean;
|
|
695
|
-
readonly isPrimal?: boolean;
|
|
696
|
-
readonly battleOnly?: T.SpeciesName | T.SpeciesName[];
|
|
697
|
-
readonly canGigantamax?: T.MoveName;
|
|
698
|
-
readonly gmaxUnreleased?: boolean;
|
|
699
|
-
readonly cannotDynamax?: boolean;
|
|
700
|
-
readonly requiredAbility?: T.AbilityName;
|
|
701
|
-
readonly requiredItem?: T.ItemName;
|
|
702
|
-
readonly requiredItems?: T.ItemName[];
|
|
703
|
-
readonly requiredMove?: T.MoveName;
|
|
704
|
-
readonly gender?: T.GenderName;
|
|
705
|
-
readonly maxHP?: number;
|
|
706
|
-
readonly evoMove?: T.MoveName;
|
|
707
|
-
readonly evoItem?: string;
|
|
708
|
-
readonly evoRegion?: 'Alola' | 'Galar';
|
|
709
|
-
readonly evoLevel?: number;
|
|
710
|
-
readonly evoCondition?: string;
|
|
711
|
-
readonly evoType?: T.EvoType;
|
|
712
|
-
readonly condition?: Partial<Condition>;
|
|
713
|
-
|
|
714
|
-
constructor(data: AnyObject) {
|
|
715
|
-
super(data);
|
|
716
|
-
data = this;
|
|
717
|
-
|
|
718
|
-
this.fullname = `pokemon: ${data.name as string}`;
|
|
719
|
-
this.effectType = 'Pokemon';
|
|
720
|
-
this.kind = 'Species';
|
|
721
|
-
|
|
722
|
-
this.baseSpecies = data.baseSpecies || data.name;
|
|
723
|
-
this.forme = data.forme || '';
|
|
724
|
-
this.baseForme = data.baseForme || '';
|
|
725
|
-
this.abilities = data.abilities || {0: ''};
|
|
726
|
-
this.types = data.types || ['???'];
|
|
727
|
-
this.prevo = data.prevo || '';
|
|
728
|
-
this.tier = data.tier || '';
|
|
729
|
-
this.doublesTier = data.doublesTier || '';
|
|
730
|
-
this.evos = data.evos || [];
|
|
731
|
-
this.nfe = !!this.evos?.length;
|
|
732
|
-
this.eggGroups = data.eggGroups || [];
|
|
733
|
-
this.canHatch = data.canHatch || false;
|
|
734
|
-
this.genderRatio = data.genderRatio || (this.gender === 'M' ? {M: 1, F: 0}
|
|
735
|
-
: this.gender === 'F' ? {M: 0, F: 1}
|
|
736
|
-
: this.gender === 'N' ? {M: 0, F: 0}
|
|
737
|
-
: {M: 0.5, F: 0.5});
|
|
738
|
-
this.requiredItem = data.requiredItem || undefined;
|
|
739
|
-
this.requiredItems =
|
|
740
|
-
this.requiredItems || (this.requiredItem ? [this.requiredItem] : undefined);
|
|
741
|
-
this.baseStats = data.baseStats || {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
|
742
|
-
this.bst = this.baseStats.hp + this.baseStats.atk + this.baseStats.def +
|
|
743
|
-
this.baseStats.spa + this.baseStats.spd + this.baseStats.spe;
|
|
744
|
-
this.weightkg = data.weightkg || 0;
|
|
745
|
-
this.weighthg = this.weightkg * 10;
|
|
746
|
-
this.tags = data.tags || [];
|
|
747
|
-
this.unreleasedHidden = data.unreleasedHidden || false;
|
|
748
|
-
this.maleOnlyHidden = !!data.maleOnlyHidden;
|
|
749
|
-
this.maxHP = data.maxHP || undefined;
|
|
750
|
-
this.isMega = !!(this.forme && ['Mega', 'Mega-X', 'Mega-Y'].includes(this.forme)) || undefined;
|
|
751
|
-
this.canGigantamax = data.canGigantamax || undefined;
|
|
752
|
-
this.gmaxUnreleased = !!data.gmaxUnreleased;
|
|
753
|
-
this.cannotDynamax = !!data.cannotDynamax;
|
|
754
|
-
this.battleOnly = data.battleOnly || (this.isMega ? this.baseSpecies : undefined);
|
|
755
|
-
this.changesFrom = data.changesFrom ||
|
|
756
|
-
(this.battleOnly !== this.baseSpecies ? this.battleOnly : this.baseSpecies);
|
|
757
|
-
if (Array.isArray(data.changesFrom)) this.changesFrom = data.changesFrom[0]; // BUG
|
|
758
|
-
|
|
759
|
-
if (!this.gen && data.num >= 1) {
|
|
760
|
-
if (data.num >= 810 || ['Gmax', 'Galar', 'Galar-Zen'].includes(this.forme)) {
|
|
761
|
-
this.gen = 8;
|
|
762
|
-
} else if (data.num >= 722 || this.forme.startsWith('Alola') || this.forme === 'Starter') {
|
|
763
|
-
this.gen = 7;
|
|
764
|
-
} else if (this.forme === 'Primal') {
|
|
765
|
-
this.gen = 6;
|
|
766
|
-
this.isPrimal = true;
|
|
767
|
-
this.battleOnly = this.baseSpecies;
|
|
768
|
-
} else if (data.num >= 650 || this.isMega) {
|
|
769
|
-
this.gen = 6;
|
|
770
|
-
} else if (data.num >= 494) {
|
|
771
|
-
this.gen = 5;
|
|
772
|
-
} else if (data.num >= 387) {
|
|
773
|
-
this.gen = 4;
|
|
774
|
-
} else if (data.num >= 252) {
|
|
775
|
-
this.gen = 3;
|
|
776
|
-
} else if (data.num >= 152) {
|
|
777
|
-
this.gen = 2;
|
|
778
|
-
} else {
|
|
779
|
-
this.gen = 1;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
type Mutable<T> = {-readonly [P in keyof T]: T[P]};
|
|
786
|
-
|
|
787
|
-
class DexSpecies implements T.DexTable<Species> {
|
|
788
|
-
readonly dex: ModdedDex;
|
|
789
|
-
readonly caches = {
|
|
790
|
-
get: Object.create(null) as { [id: string]: Species },
|
|
791
|
-
all: undefined as ReadonlyArray<Species> | undefined,
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
constructor(dex: ModdedDex) {
|
|
795
|
-
this.dex = dex;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
get(name: string): Species {
|
|
799
|
-
name = name.trim();
|
|
800
|
-
let id = toID(name);
|
|
801
|
-
if (id === 'nidoran' && name.endsWith('♀')) {
|
|
802
|
-
id = 'nidoranf' as T.ID;
|
|
803
|
-
} else if (id === 'nidoran' && name.endsWith('♂')) {
|
|
804
|
-
id = 'nidoranm' as T.ID;
|
|
805
|
-
}
|
|
806
|
-
return this.getByID(id);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
getByID(id: T.ID): Species {
|
|
810
|
-
let species: Mutable<Species> | undefined = this.caches.get[id];
|
|
811
|
-
if (species) return species;
|
|
812
|
-
|
|
813
|
-
const alias = this.dex.data.Aliases[id];
|
|
814
|
-
if (alias) {
|
|
815
|
-
const data = this.dex.data.FormatsData[id];
|
|
816
|
-
if (data) {
|
|
817
|
-
// special event ID, like Rockruff-Dusk
|
|
818
|
-
const baseId = toID(alias);
|
|
819
|
-
species = new Species({
|
|
820
|
-
...this.dex.data.Species[baseId],
|
|
821
|
-
...this.dex.data.FormatsData[id],
|
|
822
|
-
name: id,
|
|
823
|
-
});
|
|
824
|
-
species.abilities = {0: species.abilities['S']!};
|
|
825
|
-
} else {
|
|
826
|
-
species = this.get(alias);
|
|
827
|
-
if (species.cosmeticFormes) {
|
|
828
|
-
for (const forme of species.cosmeticFormes) {
|
|
829
|
-
if (toID(forme) === id) {
|
|
830
|
-
species = new Species({
|
|
831
|
-
...species,
|
|
832
|
-
name: forme,
|
|
833
|
-
id,
|
|
834
|
-
forme: forme.slice(species.name.length + 1),
|
|
835
|
-
baseForme: '',
|
|
836
|
-
baseSpecies: species.name,
|
|
837
|
-
otherFormes: null,
|
|
838
|
-
cosmeticFormes: null,
|
|
839
|
-
});
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
if (species?.exists) this.caches.get[id] = species;
|
|
847
|
-
return species;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
let data = this.dex.data.Species[id];
|
|
851
|
-
if (id && !data) {
|
|
852
|
-
let aliasTo = '';
|
|
853
|
-
const formeNames: {[k: string]: string[]} = {
|
|
854
|
-
alola: ['a', 'alola', 'alolan'],
|
|
855
|
-
galar: ['g', 'galar', 'galarian'],
|
|
856
|
-
mega: ['m', 'mega'],
|
|
857
|
-
primal: ['p', 'primal'],
|
|
858
|
-
};
|
|
859
|
-
for (const forme in formeNames) {
|
|
860
|
-
let pokeName = '';
|
|
861
|
-
for (const i of formeNames[forme]) {
|
|
862
|
-
if (id.startsWith(i)) {
|
|
863
|
-
pokeName = id.slice(i.length);
|
|
864
|
-
} else if (id.endsWith(i)) {
|
|
865
|
-
pokeName = id.slice(0, -i.length);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
if (this.dex.data.Aliases.hasOwnProperty(pokeName)) {
|
|
869
|
-
pokeName = toID(this.dex.data.Aliases[pokeName]);
|
|
870
|
-
}
|
|
871
|
-
if (this.dex.data.Species[pokeName + forme]) {
|
|
872
|
-
aliasTo = pokeName + forme;
|
|
873
|
-
break;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
if (aliasTo) {
|
|
877
|
-
species = this.get(aliasTo);
|
|
878
|
-
if (species.exists) {
|
|
879
|
-
this.caches.get[id] = species;
|
|
880
|
-
return species;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
data = this.dex.data.Species[id];
|
|
886
|
-
|
|
887
|
-
if (id && data) {
|
|
888
|
-
const tags = data.baseSpecies && this.dex.data.Species[toID(data.baseSpecies)].tags;
|
|
889
|
-
species = new Species({tags, ...data, ...this.dex.data.FormatsData[id]});
|
|
890
|
-
if (!species.tier && !species.doublesTier && species.baseSpecies !== species.name) {
|
|
891
|
-
if (species.baseSpecies === 'Mimikyu') {
|
|
892
|
-
(species as any).tier =
|
|
893
|
-
this.dex.data.FormatsData[toID(species.baseSpecies)].tier || 'Illegal';
|
|
894
|
-
(species as any).doublesTier =
|
|
895
|
-
this.dex.data.FormatsData[toID(species.baseSpecies)].doublesTier || 'Illegal';
|
|
896
|
-
} else if (species.id.endsWith('totem')) {
|
|
897
|
-
(species as any).tier =
|
|
898
|
-
this.dex.data.FormatsData[species.id.slice(0, -5)].tier || 'Illegal';
|
|
899
|
-
(species as any).doublesTier =
|
|
900
|
-
this.dex.data.FormatsData[species.id.slice(0, -5)].doublesTier || 'Illegal';
|
|
901
|
-
} else if (species.battleOnly) {
|
|
902
|
-
(species as any).tier =
|
|
903
|
-
this.dex.data.FormatsData[toID(species.battleOnly)].tier || 'Illegal';
|
|
904
|
-
(species as any).doublesTier =
|
|
905
|
-
this.dex.data.FormatsData[toID(species.battleOnly)].doublesTier || 'Illegal';
|
|
906
|
-
} else {
|
|
907
|
-
const baseFormatsData = this.dex.data.FormatsData[toID(species.baseSpecies)];
|
|
908
|
-
if (!baseFormatsData) {
|
|
909
|
-
throw new Error(`${species.baseSpecies} has no formats-data entry`);
|
|
910
|
-
}
|
|
911
|
-
(species as any).tier = baseFormatsData.tier || 'Illegal';
|
|
912
|
-
(species as any).doublesTier = baseFormatsData.doublesTier || 'Illegal';
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
if (!species.tier) species.tier = 'Illegal';
|
|
916
|
-
if (!species.doublesTier) species.doublesTier = species.tier as any;
|
|
917
|
-
if (species.gen > this.dex.gen) {
|
|
918
|
-
species.tier = 'Illegal';
|
|
919
|
-
species.doublesTier = 'Illegal';
|
|
920
|
-
species.isNonstandard = 'Future';
|
|
921
|
-
}
|
|
922
|
-
species.nfe = !!(species.evos?.length && this.get(species.evos[0]).gen <= this.dex.gen);
|
|
923
|
-
species.canHatch = species.canHatch ||
|
|
924
|
-
(!['Ditto', 'Undiscovered'].includes(
|
|
925
|
-
species.eggGroups[0]
|
|
926
|
-
) && !species.prevo && species.name !== 'Manaphy');
|
|
927
|
-
if (this.dex.gen === 1) species.bst -= species.baseStats.spd;
|
|
928
|
-
if (this.dex.gen < 5) delete species.abilities['H'];
|
|
929
|
-
} else {
|
|
930
|
-
species = new Species({
|
|
931
|
-
id, name: id,
|
|
932
|
-
exists: false, tier: 'Illegal', doublesTier: 'Illegal', isNonstandard: 'Custom',
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
if (species.exists) this.caches.get[id] = species;
|
|
937
|
-
return species;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
all(): readonly Species[] {
|
|
941
|
-
if (this.caches.all) return this.caches.all;
|
|
942
|
-
const species = [];
|
|
943
|
-
for (const id in this.dex.data.Species) {
|
|
944
|
-
species.push(this.getByID(id as T.ID));
|
|
945
|
-
}
|
|
946
|
-
return (this.caches.all = species);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
export class Learnset implements T.Learnset {
|
|
951
|
-
readonly effectType: 'Learnset';
|
|
952
|
-
readonly kind: 'Learnset';
|
|
953
|
-
readonly learnset?: { [moveid: string]: T.MoveSource[] };
|
|
954
|
-
readonly eventOnly: boolean;
|
|
955
|
-
readonly eventData?: T.EventInfo[];
|
|
956
|
-
readonly encounters?: T.EventInfo[];
|
|
957
|
-
readonly exists: boolean;
|
|
958
|
-
|
|
959
|
-
constructor(data: AnyObject) {
|
|
960
|
-
this.effectType = 'Learnset';
|
|
961
|
-
this.kind = 'Learnset';
|
|
962
|
-
this.learnset = data.learnset || undefined;
|
|
963
|
-
this.eventOnly = !!data.eventOnly;
|
|
964
|
-
this.eventData = data.eventData || undefined;
|
|
965
|
-
this.encounters = data.encounters || undefined;
|
|
966
|
-
this.exists = data.exists ?? true;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
class DexLearnsets {
|
|
971
|
-
readonly dex: ModdedDex;
|
|
972
|
-
readonly cache = Object.create(null) as { [id: string]: Learnset };
|
|
973
|
-
|
|
974
|
-
constructor(dex: ModdedDex) {
|
|
975
|
-
this.dex = dex;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
async get(name: string): Promise<Learnset> {
|
|
979
|
-
return this.getByID(toID(name));
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
async getByID(id: T.ID): Promise<Learnset> {
|
|
983
|
-
let learnset = this.cache[id];
|
|
984
|
-
if (learnset) return learnset;
|
|
985
|
-
|
|
986
|
-
if (!DATA.Learnsets) {
|
|
987
|
-
const isNode =
|
|
988
|
-
typeof process !== 'undefined' &&
|
|
989
|
-
process.versions !== null &&
|
|
990
|
-
process.versions.node !== null;
|
|
991
|
-
if (isNode) {
|
|
992
|
-
DATA.Learnsets = require('./data/learnsets.json');
|
|
993
|
-
} else {
|
|
994
|
-
try {
|
|
995
|
-
// Cast required since Typescript thinks asynchronously imported JSON have default exports
|
|
996
|
-
DATA.Learnsets =
|
|
997
|
-
(await import('./data/learnsets.json')) as unknown as Data<T.LearnsetData>;
|
|
998
|
-
} catch (err) {
|
|
999
|
-
// @ts-ignore If we're being used via a <script> tag we depend on Learnsets being required
|
|
1000
|
-
DATA.Learnsets = (window as any).DexLearnsets as Data<T.LearnsetData>;
|
|
1001
|
-
if (!DATA.Learnsets) throw new Error('Learnsets have not been included!');
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
this.dex.load('Learnsets', this.dex.modData);
|
|
1006
|
-
this.dex.modData = undefined;
|
|
1007
|
-
|
|
1008
|
-
const data = this.dex.data.Learnsets![id];
|
|
1009
|
-
if (id && data) {
|
|
1010
|
-
learnset = new Learnset(data);
|
|
1011
|
-
} else {
|
|
1012
|
-
learnset = new Learnset({exists: false});
|
|
1013
|
-
}
|
|
1014
|
-
if (learnset.exists) this.cache[id] = learnset;
|
|
1015
|
-
return learnset;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
export class Type implements T.Type {
|
|
1020
|
-
readonly id: T.ID;
|
|
1021
|
-
readonly name: T.TypeName;
|
|
1022
|
-
readonly effectType: 'Type';
|
|
1023
|
-
readonly kind: 'Type';
|
|
1024
|
-
readonly exists: boolean;
|
|
1025
|
-
readonly gen: T.GenerationNum;
|
|
1026
|
-
readonly isNonstandard: T.Nonstandard | null;
|
|
1027
|
-
readonly damageTaken: { [t in Exclude<T.TypeName, '???'>]: number } & { [key: string]: number };
|
|
1028
|
-
readonly HPivs: Partial<T.StatsTable>;
|
|
1029
|
-
readonly HPdvs: Partial<T.StatsTable>;
|
|
1030
|
-
|
|
1031
|
-
constructor(data: AnyObject) {
|
|
1032
|
-
this.exists = true;
|
|
1033
|
-
Object.assign(this, data);
|
|
1034
|
-
|
|
1035
|
-
this.effectType = 'Type';
|
|
1036
|
-
this.kind = 'Type';
|
|
1037
|
-
this.id = data.id;
|
|
1038
|
-
this.name = data.name;
|
|
1039
|
-
this.exists = !!(this.exists && this.id);
|
|
1040
|
-
this.gen = data.gen || 0;
|
|
1041
|
-
this.isNonstandard = data.isNonstandard || null;
|
|
1042
|
-
this.damageTaken = data.damageTaken || {};
|
|
1043
|
-
this.HPivs = data.HPivs || {};
|
|
1044
|
-
this.HPdvs = data.HPdvs || {};
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
toString() {
|
|
1048
|
-
return this.name;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
class DexTypes implements T.DexTable<Type> {
|
|
1053
|
-
readonly dex: ModdedDex;
|
|
1054
|
-
readonly caches = {
|
|
1055
|
-
get: Object.create(null) as { [id: string]: Type },
|
|
1056
|
-
all: undefined as ReadonlyArray<Type> | undefined,
|
|
1057
|
-
names: undefined as ReadonlyArray<string> | undefined,
|
|
1058
|
-
};
|
|
1059
|
-
|
|
1060
|
-
constructor(dex: ModdedDex) {
|
|
1061
|
-
this.dex = dex;
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
get(name: string): Type {
|
|
1065
|
-
if (name && typeof name !== 'string') return name;
|
|
1066
|
-
return this.getByID(toID(name));
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
getByID(id: T.ID): Type {
|
|
1070
|
-
const alias = this.dex.data.Aliases[id];
|
|
1071
|
-
if (alias) id = toID(alias);
|
|
1072
|
-
|
|
1073
|
-
let type = this.caches.get[id];
|
|
1074
|
-
if (type) return type;
|
|
1075
|
-
|
|
1076
|
-
const typeName = id.charAt(0).toUpperCase() + id.substr(1) as Exclude<T.TypeName, '???'>;
|
|
1077
|
-
const data = this.dex.data.Types[id];
|
|
1078
|
-
if (typeName && data) {
|
|
1079
|
-
type = new Type({name: typeName, id, ...data});
|
|
1080
|
-
} else {
|
|
1081
|
-
type = new Type({name: typeName, id, exists: false});
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
if (type.exists) this.caches.get[id] = type;
|
|
1085
|
-
return type;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
names(): readonly string[] {
|
|
1089
|
-
if (this.caches.names) return this.caches.names;
|
|
1090
|
-
this.caches.names = this.all().filter(type => !type.isNonstandard).map(type => type.name);
|
|
1091
|
-
return this.caches.names;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
isName(name: string): boolean {
|
|
1095
|
-
const id = name.toLowerCase();
|
|
1096
|
-
const typeName = id.charAt(0).toUpperCase() + id.substr(1);
|
|
1097
|
-
return name === typeName && this.dex.data.Types.hasOwnProperty(id);
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
all(): readonly Type[] {
|
|
1101
|
-
if (this.caches.all) return this.caches.all;
|
|
1102
|
-
const types = [];
|
|
1103
|
-
for (const id in this.dex.data.Types) {
|
|
1104
|
-
types.push(this.getByID(id as T.ID));
|
|
1105
|
-
}
|
|
1106
|
-
return (this.caches.all = types);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
const STATS: readonly T.StatID[] = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'];
|
|
1111
|
-
|
|
1112
|
-
class DexStats {
|
|
1113
|
-
readonly shortNames: {readonly [k in T.StatID]: string};
|
|
1114
|
-
readonly mediumNames: {readonly [k in T.StatID]: string};
|
|
1115
|
-
readonly names: {readonly [k in T.StatID]: string};
|
|
1116
|
-
|
|
1117
|
-
constructor(dex: ModdedDex) {
|
|
1118
|
-
if (dex.gen !== 1) {
|
|
1119
|
-
this.shortNames = {
|
|
1120
|
-
__proto__: null,
|
|
1121
|
-
hp: 'HP', atk: 'Atk', def: 'Def',
|
|
1122
|
-
spa: 'SpA', spd: 'SpD', spe: 'Spe',
|
|
1123
|
-
} as any;
|
|
1124
|
-
this.mediumNames = {
|
|
1125
|
-
__proto__: null,
|
|
1126
|
-
hp: 'HP', atk: 'Attack', def: 'Defense',
|
|
1127
|
-
spa: 'Sp. Atk', spd: 'Sp. Def', spe: 'Speed',
|
|
1128
|
-
} as any;
|
|
1129
|
-
this.names = {
|
|
1130
|
-
__proto__: null,
|
|
1131
|
-
hp: 'HP', atk: 'Attack', def: 'Defense',
|
|
1132
|
-
spa: 'Special Attack', spd: 'Special Defense', spe: 'Speed',
|
|
1133
|
-
} as any;
|
|
1134
|
-
} else {
|
|
1135
|
-
this.shortNames = {
|
|
1136
|
-
__proto__: null,
|
|
1137
|
-
hp: 'HP', atk: 'Atk', def: 'Def',
|
|
1138
|
-
spa: 'Spc', spd: '[SpD]', spe: 'Spe',
|
|
1139
|
-
} as any;
|
|
1140
|
-
this.mediumNames = {
|
|
1141
|
-
__proto__: null,
|
|
1142
|
-
hp: 'HP', atk: 'Attack', def: 'Defense',
|
|
1143
|
-
spa: 'Special', spd: '[Sp. Def]', spe: 'Speed',
|
|
1144
|
-
} as any;
|
|
1145
|
-
this.names = {
|
|
1146
|
-
__proto__: null,
|
|
1147
|
-
hp: 'HP', atk: 'Attack', def: 'Defense',
|
|
1148
|
-
spa: 'Special', spd: '[Special Defense]', spe: 'Speed',
|
|
1149
|
-
} as any;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
ids(): typeof STATS {
|
|
1154
|
-
return STATS;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
// #endregion
|
|
1159
|
-
|
|
1160
|
-
// #region Dex
|
|
1161
|
-
|
|
1162
|
-
type Data<T> = { 8: { [id: string]: T } } & {
|
|
1163
|
-
[num in Exclude<T.GenerationNum, 8>]?: { [id: string]: { inherit?: boolean } & T.DeepPartial<T> }
|
|
1164
|
-
};
|
|
1165
|
-
|
|
1166
|
-
const DATA = {
|
|
1167
|
-
Abilities: AbilitiesJSON as Data<T.AbilityData>,
|
|
1168
|
-
Aliases: AliasesJSON as { [id: string]: string },
|
|
1169
|
-
Conditions: ConditionsJSON as Data<T.ConditionData>,
|
|
1170
|
-
Items: ItemsJSON as Data<T.ItemData>,
|
|
1171
|
-
Moves: MovesJSON as unknown as Data<T.MoveData>,
|
|
1172
|
-
Species: SpeciesJSON as Data<T.SpeciesData>,
|
|
1173
|
-
Natures: NaturesJSON as Data<T.NatureData>,
|
|
1174
|
-
Learnsets: null! as Data<T.LearnsetData>,
|
|
1175
|
-
Types: TypesJSON as Data<T.TypeData>,
|
|
1176
|
-
FormatsData: FormatsDataJSON as Data<FormatData>,
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
|
-
const HP_TYPES: T.HPTypeName[] = [
|
|
1180
|
-
'Fighting', 'Flying', 'Poison', 'Ground', 'Rock', 'Bug', 'Ghost', 'Steel',
|
|
1181
|
-
'Fire', 'Water', 'Grass', 'Electric', 'Psychic', 'Ice', 'Dragon', 'Dark',
|
|
1182
|
-
];
|
|
1183
|
-
|
|
1184
|
-
const GEN_IDS = ['gen1', 'gen2', 'gen3', 'gen4', 'gen5', 'gen6', 'gen7', 'gen8'] as const;
|
|
1185
|
-
type GenID = typeof GEN_IDS[number];
|
|
1186
|
-
const CURRENT_GEN_ID: GenID = GEN_IDS[7];
|
|
1187
|
-
|
|
1188
|
-
const dexes: { [mod: string]: ModdedDex } = Object.create(null);
|
|
1189
|
-
|
|
1190
|
-
export type ModData = T.DeepPartial<ModdedDex['data']> & T.ModData;
|
|
1191
|
-
|
|
1192
|
-
export class ModdedDex implements T.Dex {
|
|
1193
|
-
static readonly STATS: ReadonlyArray<T.StatID> = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'];
|
|
1194
|
-
|
|
1195
|
-
readonly gen: T.GenerationNum;
|
|
1196
|
-
readonly modid: T.ID;
|
|
1197
|
-
readonly data!: {
|
|
1198
|
-
Abilities: { [id: string]: T.AbilityData };
|
|
1199
|
-
Aliases: { [id: string]: string };
|
|
1200
|
-
Conditions: { [id: string]: T.ConditionData };
|
|
1201
|
-
FormatsData: { [id: string]: FormatData };
|
|
1202
|
-
Items: { [id: string]: T.ItemData };
|
|
1203
|
-
Learnsets: null | { [id: string]: T.LearnsetData };
|
|
1204
|
-
Moves: { [id: string]: T.MoveData };
|
|
1205
|
-
Natures: { [id: string]: T.NatureData };
|
|
1206
|
-
Species: { [id: string]: T.SpeciesData };
|
|
1207
|
-
Types: { [id: string]: T.TypeData };
|
|
1208
|
-
};
|
|
1209
|
-
|
|
1210
|
-
readonly abilities: DexAbilities;
|
|
1211
|
-
readonly conditions: DexConditions;
|
|
1212
|
-
readonly items: DexItems;
|
|
1213
|
-
readonly learnsets: DexLearnsets;
|
|
1214
|
-
readonly moves: DexMoves;
|
|
1215
|
-
readonly natures: DexNatures;
|
|
1216
|
-
readonly species: DexSpecies;
|
|
1217
|
-
readonly stats: DexStats;
|
|
1218
|
-
readonly types: DexTypes;
|
|
1219
|
-
|
|
1220
|
-
/* private */ modData?: ModData = undefined;
|
|
1221
|
-
|
|
1222
|
-
constructor(modid = CURRENT_GEN_ID as GenID | T.ID, modData?: ModData) {
|
|
1223
|
-
const isGen = (GEN_IDS as unknown as Array<GenID | T.ID>).includes(modid);
|
|
1224
|
-
if (!isGen && !modData) throw new Error(`Must provide mod data with mod '${modid}'`);
|
|
1225
|
-
this.modid = modid as T.ID;
|
|
1226
|
-
this.gen = parseInt((modData?.Scripts?.inherit ?? modid).slice(3)) as T.GenerationNum || 8;
|
|
1227
|
-
this.loadData(modData);
|
|
1228
|
-
|
|
1229
|
-
this.abilities = new DexAbilities(this);
|
|
1230
|
-
this.conditions = new DexConditions(this);
|
|
1231
|
-
this.items = new DexItems(this);
|
|
1232
|
-
this.learnsets = new DexLearnsets(this);
|
|
1233
|
-
this.moves = new DexMoves(this);
|
|
1234
|
-
this.natures = new DexNatures(this);
|
|
1235
|
-
this.species = new DexSpecies(this);
|
|
1236
|
-
this.stats = new DexStats(this);
|
|
1237
|
-
this.types = new DexTypes(this);
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
mod(genid: GenID): ModdedDex;
|
|
1241
|
-
mod(modid: T.ID, modData: ModData): ModdedDex;
|
|
1242
|
-
mod(modid: GenID | T.ID, modData?: ModData) {
|
|
1243
|
-
if (modid in dexes) return modData ? new ModdedDex(modid, modData) : dexes[modid];
|
|
1244
|
-
return (dexes[modid] = new ModdedDex(modid as T.ID, modData));
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
forGen(gen: number) {
|
|
1248
|
-
if (gen < 1 || gen > 8) throw new Error(`Unsupported gen ${gen}`);
|
|
1249
|
-
return this.mod(`gen${gen}` as GenID);
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
getImmunity(
|
|
1253
|
-
source: { type: string } | string,
|
|
1254
|
-
target: { getTypes: () => string[] } | { types: string[] } | string[] | string
|
|
1255
|
-
): boolean {
|
|
1256
|
-
const sourceType: string = typeof source !== 'string' ? source.type : source;
|
|
1257
|
-
// @ts-ignore
|
|
1258
|
-
const targetTyping: string[] | string = target.getTypes?.() || target.types || target;
|
|
1259
|
-
if (Array.isArray(targetTyping)) {
|
|
1260
|
-
for (const type of targetTyping) {
|
|
1261
|
-
if (!this.getImmunity(sourceType, type)) return false;
|
|
1262
|
-
}
|
|
1263
|
-
return true;
|
|
1264
|
-
}
|
|
1265
|
-
const typeData = this.types.get(targetTyping as Exclude<T.TypeName, '???'>);
|
|
1266
|
-
if (typeData && typeData.damageTaken[sourceType] === 3) return false;
|
|
1267
|
-
return true;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
getEffectiveness(
|
|
1271
|
-
source: { type: string } | string,
|
|
1272
|
-
target: { getTypes: () => string[] } | { types: string[] } | string[] | string
|
|
1273
|
-
): number {
|
|
1274
|
-
const sourceType: string = typeof source !== 'string' ? source.type : source;
|
|
1275
|
-
// @ts-ignore
|
|
1276
|
-
const targetTyping: string[] | string = target.getTypes?.() || target.types || target;
|
|
1277
|
-
let totalTypeMod = 0;
|
|
1278
|
-
if (Array.isArray(targetTyping)) {
|
|
1279
|
-
for (const type of targetTyping) {
|
|
1280
|
-
totalTypeMod += this.getEffectiveness(sourceType, type);
|
|
1281
|
-
}
|
|
1282
|
-
return totalTypeMod;
|
|
1283
|
-
}
|
|
1284
|
-
const typeData = this.types.get(targetTyping as Exclude<T.TypeName, '???'>);
|
|
1285
|
-
if (!typeData) return 0;
|
|
1286
|
-
switch (typeData.damageTaken[sourceType]) {
|
|
1287
|
-
case 1: return 1; // super-effective
|
|
1288
|
-
case 2: return -1; // resist
|
|
1289
|
-
// in case of weird situations like Gravity, immunity is handled elsewhere
|
|
1290
|
-
default: return 0;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
getHiddenPower(ivs: T.StatsTable) {
|
|
1295
|
-
const tr = (num: number, bits = 0) => {
|
|
1296
|
-
if (bits) return (num >>> 0) % (2 ** bits);
|
|
1297
|
-
return num >>> 0;
|
|
1298
|
-
};
|
|
1299
|
-
const stats = {hp: 31, atk: 31, def: 31, spe: 31, spa: 31, spd: 31};
|
|
1300
|
-
if (this.gen <= 2) {
|
|
1301
|
-
// Gen 2 specific Hidden Power check. IVs are still treated 0-31 so we get them 0-15
|
|
1302
|
-
const atkDV = tr(ivs.atk / 2);
|
|
1303
|
-
const defDV = tr(ivs.def / 2);
|
|
1304
|
-
const speDV = tr(ivs.spe / 2);
|
|
1305
|
-
const spcDV = tr(ivs.spa / 2);
|
|
1306
|
-
return {
|
|
1307
|
-
type: HP_TYPES[4 * (atkDV % 4) + (defDV % 4)],
|
|
1308
|
-
power: tr(
|
|
1309
|
-
(5 * ((spcDV >> 3) +
|
|
1310
|
-
(2 * (speDV >> 3)) +
|
|
1311
|
-
(4 * (defDV >> 3)) +
|
|
1312
|
-
(8 * (atkDV >> 3))) +
|
|
1313
|
-
(spcDV % 4)) / 2 + 31
|
|
1314
|
-
),
|
|
1315
|
-
};
|
|
1316
|
-
} else {
|
|
1317
|
-
// Hidden Power check for Gen 3 onwards
|
|
1318
|
-
let hpTypeX = 0;
|
|
1319
|
-
let hpPowerX = 0;
|
|
1320
|
-
let i = 1;
|
|
1321
|
-
for (const s in stats) {
|
|
1322
|
-
hpTypeX += i * (ivs[s as T.StatID] % 2);
|
|
1323
|
-
hpPowerX += i * (tr(ivs[s as T.StatID] / 2) % 2);
|
|
1324
|
-
i *= 2;
|
|
1325
|
-
}
|
|
1326
|
-
return {
|
|
1327
|
-
type: HP_TYPES[tr(hpTypeX * 15 / 63)],
|
|
1328
|
-
// After Gen 6, Hidden Power is always 60 base power
|
|
1329
|
-
power: (this.gen && this.gen < 6) ? tr(hpPowerX * 40 / 63) + 30 : 60,
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
includeModData() {
|
|
1335
|
-
return this;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
includeData() {
|
|
1339
|
-
return this;
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
includeFormats() {
|
|
1343
|
-
return this;
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
loadData(modData?: ModData) {
|
|
1347
|
-
if (this.data) return this.data;
|
|
1348
|
-
(this.data as any) = {} as ModdedDex['data'];
|
|
1349
|
-
|
|
1350
|
-
for (const t in DATA) {
|
|
1351
|
-
const type = t as keyof typeof DATA;
|
|
1352
|
-
if (type === 'Learnsets') {
|
|
1353
|
-
this.modData = modData;
|
|
1354
|
-
continue; // async
|
|
1355
|
-
}
|
|
1356
|
-
if (type === 'Aliases') {
|
|
1357
|
-
(this.data as any)[type] = DATA[type];
|
|
1358
|
-
continue;
|
|
1359
|
-
}
|
|
1360
|
-
this.load(type, modData);
|
|
1361
|
-
}
|
|
1362
|
-
return this.data;
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
load(type: Exclude<keyof ModdedDex['data'], 'Aliases'>, modData?: ModData) {
|
|
1366
|
-
if (this.data[type]) return;
|
|
1367
|
-
|
|
1368
|
-
const d = modData ? modData[type] : DATA[type][this.gen];
|
|
1369
|
-
if (d !== this.data[type]) this.data[type] = ({...d, ...this.data[type]}) as any;
|
|
1370
|
-
|
|
1371
|
-
if (this.modid === CURRENT_GEN_ID) return;
|
|
1372
|
-
|
|
1373
|
-
const parentDex = modData?.Scripts?.inherit
|
|
1374
|
-
? this.mod(modData.Scripts.inherit)
|
|
1375
|
-
: this.forGen(modData ? this.gen : this.gen + 1 as T.GenerationNum);
|
|
1376
|
-
if (type === 'Learnsets') parentDex.load('Learnsets');
|
|
1377
|
-
|
|
1378
|
-
const parentDataType = parentDex.data[type];
|
|
1379
|
-
const childDataType = this.data[type] || (this.data[type] = {} as any);
|
|
1380
|
-
for (const e in parentDataType) {
|
|
1381
|
-
const entry = e as keyof typeof parentDataType;
|
|
1382
|
-
if (childDataType[entry] === null) {
|
|
1383
|
-
// null means don't inherit
|
|
1384
|
-
delete childDataType[entry];
|
|
1385
|
-
} else if (!(entry in childDataType)) {
|
|
1386
|
-
// If it doesn't exist it's inherited from the parent data
|
|
1387
|
-
if (type === 'Species') {
|
|
1388
|
-
// Species entries can be modified too many different ways
|
|
1389
|
-
// e.g. inheriting different formats-data/learnsets
|
|
1390
|
-
childDataType[entry] = deepClone(parentDataType[entry]);
|
|
1391
|
-
} else {
|
|
1392
|
-
childDataType[entry] = parentDataType[entry];
|
|
1393
|
-
}
|
|
1394
|
-
} else if (childDataType[entry]?.inherit) {
|
|
1395
|
-
// {inherit: true} can be used to modify only parts of the parent data,
|
|
1396
|
-
// instead of overwriting entirely
|
|
1397
|
-
delete childDataType[entry].inherit;
|
|
1398
|
-
// Merge parent into children entry, preserving existing childs' properties.
|
|
1399
|
-
for (const key in parentDataType[entry]) {
|
|
1400
|
-
if (key in childDataType[entry]) continue;
|
|
1401
|
-
(childDataType[entry])[key] = (parentDataType[entry] as any)[key];
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
const SPECIAL = ['Fire', 'Water', 'Grass', 'Electric', 'Ice', 'Psychic', 'Dark', 'Dragon'];
|
|
1409
|
-
function getGen3Category(type: T.TypeName) {
|
|
1410
|
-
return SPECIAL.includes(type) ? 'Special' : 'Physical';
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
function deepClone(obj: any): any {
|
|
1414
|
-
if (obj === null || typeof obj !== 'object') return obj;
|
|
1415
|
-
if (Array.isArray(obj)) return obj.map(prop => deepClone(prop));
|
|
1416
|
-
const clone = Object.create(Object.getPrototypeOf(obj));
|
|
1417
|
-
for (const key of Object.keys(obj)) {
|
|
1418
|
-
clone[key] = deepClone(obj[key]);
|
|
1419
|
-
}
|
|
1420
|
-
return clone;
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// #endregion
|
|
1424
|
-
|
|
1425
|
-
dexes[CURRENT_GEN_ID] = new ModdedDex(CURRENT_GEN_ID);
|
|
1426
|
-
export const Dex = dexes[CURRENT_GEN_ID];
|
|
1427
|
-
|
|
1428
|
-
export {
|
|
1429
|
-
ID,
|
|
1430
|
-
As,
|
|
1431
|
-
Weather,
|
|
1432
|
-
FieldCondition,
|
|
1433
|
-
SideCondition,
|
|
1434
|
-
GenerationNum,
|
|
1435
|
-
GenderName,
|
|
1436
|
-
StatID,
|
|
1437
|
-
StatsTable,
|
|
1438
|
-
BoostID,
|
|
1439
|
-
BoostsTable,
|
|
1440
|
-
MoveCategory,
|
|
1441
|
-
MoveTarget,
|
|
1442
|
-
Nonstandard,
|
|
1443
|
-
EvoType,
|
|
1444
|
-
EggGroup,
|
|
1445
|
-
SideID,
|
|
1446
|
-
Player,
|
|
1447
|
-
GameType,
|
|
1448
|
-
HPColor,
|
|
1449
|
-
StatusName,
|
|
1450
|
-
NatureName,
|
|
1451
|
-
TypeName,
|
|
1452
|
-
HPTypeName,
|
|
1453
|
-
Tier,
|
|
1454
|
-
PokemonSet,
|
|
1455
|
-
AbilityName,
|
|
1456
|
-
ItemName,
|
|
1457
|
-
MoveName,
|
|
1458
|
-
SpeciesName,
|
|
1459
|
-
FormeName,
|
|
1460
|
-
EffectType,
|
|
1461
|
-
Effect,
|
|
1462
|
-
DataKind,
|
|
1463
|
-
Data,
|
|
1464
|
-
EffectData,
|
|
1465
|
-
HitEffect,
|
|
1466
|
-
SecondaryEffect,
|
|
1467
|
-
ConditionData,
|
|
1468
|
-
AbilityData,
|
|
1469
|
-
ItemData,
|
|
1470
|
-
MoveData,
|
|
1471
|
-
SpeciesData,
|
|
1472
|
-
MoveSource,
|
|
1473
|
-
EventInfoData,
|
|
1474
|
-
LearnsetData,
|
|
1475
|
-
TypeData,
|
|
1476
|
-
NatureData,
|
|
1477
|
-
// BasicEffect,
|
|
1478
|
-
// Condition,
|
|
1479
|
-
// Ability,
|
|
1480
|
-
// Item,
|
|
1481
|
-
// Move,
|
|
1482
|
-
// Species,
|
|
1483
|
-
EventInfo,
|
|
1484
|
-
// Learnset,
|
|
1485
|
-
// Type,
|
|
1486
|
-
// Nature,
|
|
1487
|
-
GenID,
|
|
1488
|
-
// Dex,
|
|
1489
|
-
} from '@pkmn/dex-types';
|