koffing 1.0.0 → 2.0.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/README.md +94 -7
- package/dist/data-CP51LAc9.cjs +404 -0
- package/dist/data-CRHBhbCJ.js +345 -0
- package/dist/index.cjs +441 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +27 -104
- package/dist/index.js +432 -430
- package/dist/types-BVzP_Ko4.d.cts +62 -0
- package/dist/types-BVzP_Ko4.d.ts +62 -0
- package/dist/validator.cjs +3563 -0
- package/dist/validator.d.cts +14 -0
- package/dist/validator.d.ts +14 -0
- package/dist/validator.js +3562 -0
- package/package.json +43 -22
- package/dist/index.mjs +0 -400
package/dist/index.js
CHANGED
|
@@ -1,431 +1,433 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { a as KoffingError, c as finish, i as DEFAULT_LIMITS, l as report, n as sanitizeTeam, o as checkInput, r as validateTeam, s as fail, t as parseJSON, u as resolveLimits } from "./data-CRHBhbCJ.js";
|
|
2
|
+
//#region src/parse.ts
|
|
3
|
+
const stats$1 = {
|
|
4
|
+
hp: "hp",
|
|
5
|
+
atk: "atk",
|
|
6
|
+
attack: "atk",
|
|
7
|
+
def: "def",
|
|
8
|
+
defense: "def",
|
|
9
|
+
spa: "spa",
|
|
10
|
+
spatk: "spa",
|
|
11
|
+
specialattack: "spa",
|
|
12
|
+
spd: "spd",
|
|
13
|
+
spdef: "spd",
|
|
14
|
+
specialdefense: "spd",
|
|
15
|
+
spe: "spe",
|
|
16
|
+
speed: "spe"
|
|
9
17
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
Koffing,
|
|
427
|
-
Pokemon,
|
|
428
|
-
PokemonTeam,
|
|
429
|
-
PokemonTeamSet,
|
|
430
|
-
ShowdownParser
|
|
431
|
-
});
|
|
18
|
+
const numericFields = /* @__PURE__ */ new Map([
|
|
19
|
+
["level", "level"],
|
|
20
|
+
["happiness", "happiness"],
|
|
21
|
+
["friendship", "happiness"],
|
|
22
|
+
["dynamax level", "dynamaxLevel"]
|
|
23
|
+
]);
|
|
24
|
+
const stringFields = /* @__PURE__ */ new Map([
|
|
25
|
+
["ability", "ability"],
|
|
26
|
+
["trait", "ability"],
|
|
27
|
+
["item", "item"],
|
|
28
|
+
["nickname", "name"],
|
|
29
|
+
["species", "species"],
|
|
30
|
+
["tera type", "teraType"],
|
|
31
|
+
["hidden power", "hpType"],
|
|
32
|
+
["pokeball", "pokeball"],
|
|
33
|
+
["ball", "pokeball"]
|
|
34
|
+
]);
|
|
35
|
+
const natureStats = [
|
|
36
|
+
"atk",
|
|
37
|
+
"def",
|
|
38
|
+
"spe",
|
|
39
|
+
"spa",
|
|
40
|
+
"spd"
|
|
41
|
+
];
|
|
42
|
+
const natures = [
|
|
43
|
+
[
|
|
44
|
+
"Hardy",
|
|
45
|
+
"Lonely",
|
|
46
|
+
"Brave",
|
|
47
|
+
"Adamant",
|
|
48
|
+
"Naughty"
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
"Bold",
|
|
52
|
+
"Docile",
|
|
53
|
+
"Relaxed",
|
|
54
|
+
"Impish",
|
|
55
|
+
"Lax"
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"Timid",
|
|
59
|
+
"Hasty",
|
|
60
|
+
"Serious",
|
|
61
|
+
"Jolly",
|
|
62
|
+
"Naive"
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
"Modest",
|
|
66
|
+
"Mild",
|
|
67
|
+
"Quiet",
|
|
68
|
+
"Bashful",
|
|
69
|
+
"Rash"
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"Calm",
|
|
73
|
+
"Gentle",
|
|
74
|
+
"Sassy",
|
|
75
|
+
"Careful",
|
|
76
|
+
"Quirky"
|
|
77
|
+
]
|
|
78
|
+
];
|
|
79
|
+
const controlCharacters = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
80
|
+
/** Parse Showdown text without silently clamping values or dropping extra moves. */
|
|
81
|
+
function parse(input, options = {}) {
|
|
82
|
+
const limits = resolveLimits(options);
|
|
83
|
+
checkInput(input, limits);
|
|
84
|
+
const hasControls = controlCharacters.test(input);
|
|
85
|
+
const hasTabs = input.includes(" ");
|
|
86
|
+
const hasPipes = input.includes("|");
|
|
87
|
+
const teams = [];
|
|
88
|
+
const diagnostics = [];
|
|
89
|
+
let team;
|
|
90
|
+
let pokemon;
|
|
91
|
+
let count = 0;
|
|
92
|
+
let seen = /* @__PURE__ */ new Set();
|
|
93
|
+
let explicitHpType = false;
|
|
94
|
+
let explicitHappiness = false;
|
|
95
|
+
let explicitNature = false;
|
|
96
|
+
let increased;
|
|
97
|
+
let decreased;
|
|
98
|
+
let lineNumber = 0;
|
|
99
|
+
const warn = (code, message) => {
|
|
100
|
+
report(diagnostics, {
|
|
101
|
+
code,
|
|
102
|
+
message,
|
|
103
|
+
severity: "warning",
|
|
104
|
+
line: lineNumber
|
|
105
|
+
}, limits);
|
|
106
|
+
};
|
|
107
|
+
const duplicate = (key) => {
|
|
108
|
+
if (seen.has(key)) warn("duplicate-field", `Repeated ${key}; the last value takes precedence`);
|
|
109
|
+
seen.add(key);
|
|
110
|
+
};
|
|
111
|
+
const addTeam = (value) => {
|
|
112
|
+
if (teams.length >= limits.maxTeams) fail("team-limit", "Too many teams", lineNumber);
|
|
113
|
+
teams.push(value);
|
|
114
|
+
team = value;
|
|
115
|
+
};
|
|
116
|
+
const move = (value) => {
|
|
117
|
+
if (!pokemon) return;
|
|
118
|
+
if (!value) {
|
|
119
|
+
warn("invalid-move", "Move name is empty");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (pokemon.moves.length >= limits.maxMoves) fail("move-limit", "Too many moves", lineNumber);
|
|
123
|
+
const hidden = /^Hidden Power\s*\[([^\]]+)\]$/i.exec(value);
|
|
124
|
+
if (hidden) {
|
|
125
|
+
value = `Hidden Power ${hidden[1].trim()}`;
|
|
126
|
+
if (!explicitHpType) pokemon.hpType = hidden[1].trim();
|
|
127
|
+
}
|
|
128
|
+
pokemon.moves.push(value);
|
|
129
|
+
if (pokemon.moves.length === 5) warn("move-count", "More than four moves; all moves were preserved");
|
|
130
|
+
if (/^Frustration$/i.test(value) && !explicitHappiness) pokemon.happiness = 0;
|
|
131
|
+
};
|
|
132
|
+
let nextCR = input.indexOf("\r");
|
|
133
|
+
let nextLF = input.indexOf("\n");
|
|
134
|
+
for (let start = 0; start <= input.length;) {
|
|
135
|
+
let end = nextLF < 0 ? input.length : nextLF;
|
|
136
|
+
if (nextCR >= 0 && nextCR < end) end = nextCR;
|
|
137
|
+
lineNumber++;
|
|
138
|
+
if (end - start > limits.maxLineLength) fail("line-limit", "Line is too long", lineNumber);
|
|
139
|
+
const rawLine = input.slice(start, end);
|
|
140
|
+
const line = (hasTabs ? rawLine.replace(/\t/g, " ") : rawLine).trim();
|
|
141
|
+
const separator = input[end];
|
|
142
|
+
start = end + (separator === "\r" && input[end + 1] === "\n" ? 2 : 1);
|
|
143
|
+
if (nextCR >= 0 && nextCR < start) nextCR = input.indexOf("\r", start);
|
|
144
|
+
if (nextLF >= 0 && nextLF < start) nextLF = input.indexOf("\n", start);
|
|
145
|
+
if (hasControls && controlCharacters.test(rawLine)) {
|
|
146
|
+
warn("invalid-character", "Control characters are not allowed");
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!line || /^-{3,}$/.test(line)) {
|
|
150
|
+
pokemon = void 0;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (hasPipes && line.includes("|")) fail("unsupported-packed-format", "Packed teams are not supported; import Showdown text instead", lineNumber);
|
|
154
|
+
if (pokemon && (line[0] === "-" || line[0] === "~")) {
|
|
155
|
+
move(line.slice(1).trimStart());
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const header = line[0] === "=" ? /^===\s*(.*?)\s*===$/.exec(line) : null;
|
|
159
|
+
if (header) {
|
|
160
|
+
const value = { pokemon: [] };
|
|
161
|
+
let title = header[1].trim();
|
|
162
|
+
const format = /^\[([^\]]+)\]\s*/.exec(title);
|
|
163
|
+
if (format) {
|
|
164
|
+
value.format = format[1].trim();
|
|
165
|
+
title = title.slice(format[0].length);
|
|
166
|
+
}
|
|
167
|
+
const slash = title.lastIndexOf("/");
|
|
168
|
+
if (slash >= 0) {
|
|
169
|
+
value.folder = title.slice(0, slash).trim();
|
|
170
|
+
title = title.slice(slash + 1).trim();
|
|
171
|
+
}
|
|
172
|
+
if (title) value.name = title;
|
|
173
|
+
addTeam(value);
|
|
174
|
+
pokemon = void 0;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (line.startsWith("===")) {
|
|
178
|
+
warn("invalid-header", "Malformed team header");
|
|
179
|
+
pokemon = void 0;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const colon = line.indexOf(":");
|
|
183
|
+
const hasDetail = colon > 0;
|
|
184
|
+
const nature = hasDetail || line.includes("@") || !/\sNature$/i.test(line) ? null : /^(.*?)\s+Nature$/i.exec(line);
|
|
185
|
+
const bare = !hasDetail && /^(Shiny|Gigantamax)$/i.test(line);
|
|
186
|
+
if (!pokemon) {
|
|
187
|
+
const typeNullHeader = hasDetail && /^(?:[^:]*\()?Type: Null\)?(?:\s+\([MFN]\))?(?:\s+\[[^\]]+\])?(?:\s+@\s+.+)?$/i.test(line);
|
|
188
|
+
if (line.startsWith("-") || hasDetail && !typeNullHeader || nature || bare) {
|
|
189
|
+
warn("orphan-detail", "Pokémon detail appears before a Pokémon header");
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
let identity = line;
|
|
193
|
+
let item;
|
|
194
|
+
const at = identity.lastIndexOf(" @ ");
|
|
195
|
+
if (at >= 0) {
|
|
196
|
+
item = identity.slice(at + 3).trim();
|
|
197
|
+
identity = identity.slice(0, at).trim();
|
|
198
|
+
}
|
|
199
|
+
let ability;
|
|
200
|
+
const bracket = /\s*\[([^\]]+)\]$/.exec(identity);
|
|
201
|
+
if (bracket) {
|
|
202
|
+
ability = bracket[1].trim();
|
|
203
|
+
identity = identity.slice(0, bracket.index).trim();
|
|
204
|
+
}
|
|
205
|
+
let gender;
|
|
206
|
+
const sex = /\s*\(([MFN])\)$/.exec(identity);
|
|
207
|
+
if (sex) {
|
|
208
|
+
gender = sex[1];
|
|
209
|
+
identity = identity.slice(0, sex.index).trim();
|
|
210
|
+
}
|
|
211
|
+
const named = /^(.*?)\s*\(([^()]+)\)$/.exec(identity);
|
|
212
|
+
const species = (named ? named[2] : identity).trim();
|
|
213
|
+
if (!species || /[\[\]@()=]/.test(species)) {
|
|
214
|
+
warn("invalid-header", "Malformed Pokémon header");
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (count >= limits.maxPokemon) fail("pokemon-limit", "Too many Pokémon", lineNumber);
|
|
218
|
+
count++;
|
|
219
|
+
pokemon = {
|
|
220
|
+
species,
|
|
221
|
+
moves: []
|
|
222
|
+
};
|
|
223
|
+
if (named?.[1]?.trim()) pokemon.name = named[1].trim();
|
|
224
|
+
if (item && !/^No Item$/i.test(item)) pokemon.item = item;
|
|
225
|
+
if (ability) pokemon.ability = ability;
|
|
226
|
+
if (gender) pokemon.gender = gender;
|
|
227
|
+
if (!team) addTeam({ pokemon: [] });
|
|
228
|
+
team.pokemon.push(pokemon);
|
|
229
|
+
seen.clear();
|
|
230
|
+
seen.add("species");
|
|
231
|
+
if (pokemon.name !== void 0) seen.add("name");
|
|
232
|
+
if (pokemon.item !== void 0) seen.add("item");
|
|
233
|
+
if (pokemon.ability !== void 0) seen.add("ability");
|
|
234
|
+
if (pokemon.gender !== void 0) seen.add("gender");
|
|
235
|
+
explicitHpType = false;
|
|
236
|
+
explicitHappiness = false;
|
|
237
|
+
explicitNature = false;
|
|
238
|
+
increased = void 0;
|
|
239
|
+
decreased = void 0;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const bracketDetail = line[0] === "[" ? /^\[([^\]]+)\](?:\s*@\s*(.+))?$/.exec(line) : null;
|
|
243
|
+
if (bracketDetail) {
|
|
244
|
+
duplicate("ability");
|
|
245
|
+
pokemon.ability = bracketDetail[1].trim();
|
|
246
|
+
if (bracketDetail[2]) {
|
|
247
|
+
duplicate("item");
|
|
248
|
+
if (/^No Item$/i.test(bracketDetail[2])) delete pokemon.item;
|
|
249
|
+
else pokemon.item = bracketDetail[2].trim();
|
|
250
|
+
}
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (nature) {
|
|
254
|
+
duplicate("nature");
|
|
255
|
+
pokemon.nature = nature[1].trim();
|
|
256
|
+
explicitNature = true;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const key = (hasDetail ? line.slice(0, colon).trim() : bare ? line : "").toLowerCase();
|
|
260
|
+
const value = hasDetail ? line.slice(colon + 1).trim() : "Yes";
|
|
261
|
+
if (key === "move") {
|
|
262
|
+
move(value);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (key === "evs" || key === "ivs") {
|
|
266
|
+
duplicate(key);
|
|
267
|
+
const values = {};
|
|
268
|
+
let conflictingModifiers = false;
|
|
269
|
+
if (key === "evs") {
|
|
270
|
+
increased = void 0;
|
|
271
|
+
decreased = void 0;
|
|
272
|
+
if (!explicitNature) delete pokemon.nature;
|
|
273
|
+
}
|
|
274
|
+
for (const entry of value.split("/")) {
|
|
275
|
+
const match = /^\s*([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)([+-]?)\s+([a-z .]+)\s*$/i.exec(entry);
|
|
276
|
+
const statName = match?.[3].toLowerCase().replace(/[ .]/g, "");
|
|
277
|
+
const stat = statName && Object.hasOwn(stats$1, statName) ? stats$1[statName] : void 0;
|
|
278
|
+
if (!match || !stat || !Number.isFinite(Number(match[1]))) {
|
|
279
|
+
warn("invalid-stat", `Invalid ${key} entry: ${entry.trim()}`);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (values[stat] !== void 0) warn("duplicate-stat", `Repeated ${stat}; the last value takes precedence`);
|
|
283
|
+
values[stat] = Number(match[1]);
|
|
284
|
+
const max = key === "ivs" ? 31 : 255;
|
|
285
|
+
if (!Number.isInteger(values[stat]) || values[stat] < 0 || values[stat] > max) warn("stat-range", `${stat} is outside the traditional ${key} range; value preserved`);
|
|
286
|
+
if (match[2]) {
|
|
287
|
+
if (key !== "evs" || stat === "hp") warn("invalid-nature-modifier", "Nature modifiers apply only to non-HP EVs");
|
|
288
|
+
else if (match[2] === "+") {
|
|
289
|
+
if (increased) {
|
|
290
|
+
conflictingModifiers = true;
|
|
291
|
+
warn("invalid-nature-modifier", "More than one increased stat");
|
|
292
|
+
}
|
|
293
|
+
increased = stat;
|
|
294
|
+
} else {
|
|
295
|
+
if (decreased) {
|
|
296
|
+
conflictingModifiers = true;
|
|
297
|
+
warn("invalid-nature-modifier", "More than one decreased stat");
|
|
298
|
+
}
|
|
299
|
+
decreased = stat;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
pokemon[key] = values;
|
|
304
|
+
if (key === "evs" && Object.values(values).reduce((sum, number) => sum + number, 0) > 510) warn("ev-total", "EV total exceeds 510; values preserved");
|
|
305
|
+
if (key === "evs" && increased && increased === decreased) {
|
|
306
|
+
conflictingModifiers = true;
|
|
307
|
+
warn("invalid-nature-modifier", "The same stat cannot be increased and decreased");
|
|
308
|
+
}
|
|
309
|
+
if (key === "evs" && !explicitNature && !conflictingModifiers && increased && decreased) pokemon.nature = natures[natureStats.indexOf(increased)][natureStats.indexOf(decreased)];
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const numberKey = numericFields.get(key);
|
|
313
|
+
if (numberKey) {
|
|
314
|
+
duplicate(numberKey);
|
|
315
|
+
const number = Number(value);
|
|
316
|
+
if (!value || !Number.isFinite(number)) {
|
|
317
|
+
warn("invalid-number", `Invalid ${key}`);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
pokemon[numberKey] = number;
|
|
321
|
+
if (numberKey === "happiness") explicitHappiness = true;
|
|
322
|
+
const min = numberKey === "level" ? 1 : 0;
|
|
323
|
+
const max = numberKey === "level" ? 100 : numberKey === "happiness" ? 255 : 10;
|
|
324
|
+
if (!Number.isInteger(number) || number < min || number > max) warn("number-range", `${key} is outside its traditional range; value preserved`);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (key === "shiny" || key === "gigantamax") {
|
|
328
|
+
duplicate(key);
|
|
329
|
+
if (!/^(yes|no|true|false)$/i.test(value)) warn("invalid-boolean", `Invalid ${key}`);
|
|
330
|
+
else pokemon[key] = /^(yes|true)$/i.test(value);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (key === "gender") {
|
|
334
|
+
duplicate("gender");
|
|
335
|
+
if (value === "M" || value === "F" || value === "N" || value === "") pokemon.gender = value;
|
|
336
|
+
else warn("invalid-gender", "Gender must be M, F, N or empty");
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const stringKey = stringFields.get(key);
|
|
340
|
+
if (stringKey) {
|
|
341
|
+
duplicate(stringKey);
|
|
342
|
+
if (!value) {
|
|
343
|
+
warn("empty-field", `Empty ${key}`);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (stringKey === "item" && /^No Item$/i.test(value)) delete pokemon.item;
|
|
347
|
+
else pokemon[stringKey] = value;
|
|
348
|
+
if (stringKey === "hpType") explicitHpType = true;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
warn("unknown-line", `Unrecognized line: ${line}`);
|
|
352
|
+
}
|
|
353
|
+
finish(diagnostics, options);
|
|
354
|
+
return {
|
|
355
|
+
teams,
|
|
356
|
+
diagnostics
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/serialize.ts
|
|
361
|
+
const stats = [
|
|
362
|
+
["hp", "HP"],
|
|
363
|
+
["atk", "Atk"],
|
|
364
|
+
["def", "Def"],
|
|
365
|
+
["spa", "SpA"],
|
|
366
|
+
["spd", "SpD"],
|
|
367
|
+
["spe", "Spe"]
|
|
368
|
+
];
|
|
369
|
+
function decimal(value) {
|
|
370
|
+
const source = String(value);
|
|
371
|
+
if (!/[eE]/u.test(source)) return source;
|
|
372
|
+
const [mantissa, exponentText] = source.split(/[eE]/u);
|
|
373
|
+
const negative = mantissa.startsWith("-");
|
|
374
|
+
const [whole, fraction = ""] = (negative ? mantissa.slice(1) : mantissa).split(".");
|
|
375
|
+
const digits = whole + fraction;
|
|
376
|
+
const point = whole.length + Number(exponentText);
|
|
377
|
+
const expanded = point <= 0 ? `0.${"0".repeat(-point)}${digits}` : point >= digits.length ? `${digits}${"0".repeat(point - digits.length)}` : `${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
378
|
+
return `${negative ? "-" : ""}${expanded}`;
|
|
379
|
+
}
|
|
380
|
+
function serializeSet(set) {
|
|
381
|
+
let title = set.name ? `${set.name} (${set.species})` : set.species;
|
|
382
|
+
if (set.gender === "M" || set.gender === "F") title += ` (${set.gender})`;
|
|
383
|
+
if (set.item) title += ` @ ${set.item}`;
|
|
384
|
+
let output = title;
|
|
385
|
+
const append = (label, value) => {
|
|
386
|
+
if (value !== void 0 && (typeof value !== "number" || Number.isFinite(value))) output += `\n${label}: ${value}`;
|
|
387
|
+
};
|
|
388
|
+
append("Ability", set.ability);
|
|
389
|
+
append("Level", set.level);
|
|
390
|
+
if (set.gender === "N" || set.gender === "") append("Gender", set.gender);
|
|
391
|
+
if (set.shiny !== void 0) append("Shiny", set.shiny ? "Yes" : "No");
|
|
392
|
+
append("Happiness", set.happiness);
|
|
393
|
+
append("Pokeball", set.pokeball);
|
|
394
|
+
append("Hidden Power", set.hpType);
|
|
395
|
+
append("Dynamax Level", set.dynamaxLevel);
|
|
396
|
+
if (set.gigantamax !== void 0) append("Gigantamax", set.gigantamax ? "Yes" : "No");
|
|
397
|
+
append("Tera Type", set.teraType);
|
|
398
|
+
const appendStats = (label, values) => {
|
|
399
|
+
if (values === void 0) return;
|
|
400
|
+
let entries = "";
|
|
401
|
+
for (const [key, name] of stats) {
|
|
402
|
+
const value = values[key];
|
|
403
|
+
if (value !== void 0 && Number.isFinite(value)) {
|
|
404
|
+
if (entries) entries += " / ";
|
|
405
|
+
entries += `${decimal(value)} ${name}`;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (entries) append(label, entries);
|
|
409
|
+
};
|
|
410
|
+
appendStats("EVs", set.evs);
|
|
411
|
+
if (set.nature) output += `\n${set.nature} Nature`;
|
|
412
|
+
appendStats("IVs", set.ivs);
|
|
413
|
+
for (const move of set.moves) {
|
|
414
|
+
const hidden = /^Hidden Power ([a-z]+)$/iu.exec(move);
|
|
415
|
+
output += `\n- ${hidden ? `Hidden Power [${hidden[1]}]` : move}`;
|
|
416
|
+
}
|
|
417
|
+
return output;
|
|
418
|
+
}
|
|
419
|
+
/** Format trusted typed sets, like Showdown. Use parseJSON separately for untrusted data. */
|
|
420
|
+
function exportTeam(pokemon) {
|
|
421
|
+
return pokemon.map(serializeSet).join("\n\n");
|
|
422
|
+
}
|
|
423
|
+
/** Collection export always includes headers, preserving team boundaries. */
|
|
424
|
+
function exportTeams(teams) {
|
|
425
|
+
return teams.map((team) => {
|
|
426
|
+
const metadata = `${team.format ? `[${team.format}] ` : ""}${team.folder ? `${team.folder}/` : ""}${team.name ?? ""}`;
|
|
427
|
+
const body = team.pokemon.map(serializeSet).join("\n\n");
|
|
428
|
+
if (teams.length === 1 && !metadata && body) return body;
|
|
429
|
+
return `=== ${metadata} ===${body ? `\n\n${body}` : ""}`;
|
|
430
|
+
}).join("\n\n");
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
export { DEFAULT_LIMITS, KoffingError, exportTeam, exportTeams, parse, parseJSON, sanitizeTeam, validateTeam };
|