@pkmn/engine 0.1.0-dev.02b7f810

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.
Files changed (52) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +332 -0
  3. package/build/pkg/addon.d.ts +7 -0
  4. package/build/pkg/addon.js +111 -0
  5. package/build/pkg/addon.js.map +1 -0
  6. package/build/pkg/data/ids.json +240 -0
  7. package/build/pkg/data/layout.json +86 -0
  8. package/build/pkg/data/protocol.json +173 -0
  9. package/build/pkg/data.d.ts +91 -0
  10. package/build/pkg/data.js +150 -0
  11. package/build/pkg/data.js.map +1 -0
  12. package/build/pkg/gen1.d.ts +109 -0
  13. package/build/pkg/gen1.js +668 -0
  14. package/build/pkg/gen1.js.map +1 -0
  15. package/build/pkg/index.d.ts +383 -0
  16. package/build/pkg/index.js +122 -0
  17. package/build/pkg/index.js.map +1 -0
  18. package/build/pkg/protocol.d.ts +71 -0
  19. package/build/pkg/protocol.js +452 -0
  20. package/build/pkg/protocol.js.map +1 -0
  21. package/build/test/display.js +382 -0
  22. package/build/test/display.js.map +1 -0
  23. package/build/tools/debug.js +133 -0
  24. package/build/tools/debug.js.map +1 -0
  25. package/build.zig +346 -0
  26. package/package.json +202 -0
  27. package/src/bin/install-pkmn-engine +391 -0
  28. package/src/bin/pkmn-debug +62 -0
  29. package/src/lib/binding/node.zig +229 -0
  30. package/src/lib/binding/wasm.zig +10 -0
  31. package/src/lib/common/data.zig +129 -0
  32. package/src/lib/common/debug.zig +64 -0
  33. package/src/lib/common/options.zig +40 -0
  34. package/src/lib/common/protocol.zig +1297 -0
  35. package/src/lib/common/rng.zig +234 -0
  36. package/src/lib/gen1/README.md +791 -0
  37. package/src/lib/gen1/data/moves.zig +1854 -0
  38. package/src/lib/gen1/data/species.zig +1105 -0
  39. package/src/lib/gen1/data/types.zig +131 -0
  40. package/src/lib/gen1/data.zig +620 -0
  41. package/src/lib/gen1/helpers.zig +234 -0
  42. package/src/lib/gen1/mechanics.zig +2819 -0
  43. package/src/lib/gen2/README.md +309 -0
  44. package/src/lib/gen2/data/items.zig +236 -0
  45. package/src/lib/gen2/data/moves.zig +2784 -0
  46. package/src/lib/gen2/data/species.zig +1798 -0
  47. package/src/lib/gen2/data/types.zig +123 -0
  48. package/src/lib/gen2/data.zig +425 -0
  49. package/src/lib/gen2/helpers.zig +225 -0
  50. package/src/lib/gen2/mechanics.zig +53 -0
  51. package/src/lib/pkmn.zig +54 -0
  52. package/src/test/display/pkmn.html.tmpl +368 -0
@@ -0,0 +1,668 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.StoredPokemon = exports.Pokemon = exports.Side = exports.Battle = void 0;
27
+ const data_1 = require("@pkmn/data");
28
+ const addon = __importStar(require("./addon"));
29
+ const data_2 = require("./data");
30
+ const protocol_1 = require("./protocol");
31
+ const SIZES = data_2.LAYOUT[0].sizes;
32
+ const OFFSETS = data_2.LAYOUT[0].offsets;
33
+ OFFSETS.Stats.spa = OFFSETS.Stats.spd = OFFSETS.Stats.spc;
34
+ OFFSETS.Boosts.spa = OFFSETS.Boosts.spd = OFFSETS.Boosts.spc;
35
+ const VOLATILES = {};
36
+ for (const v in OFFSETS.Volatiles) {
37
+ if (v.charCodeAt(0) >= 65 && v.charCodeAt(0) <= 90) {
38
+ VOLATILES[v] = OFFSETS.Volatiles[v];
39
+ }
40
+ }
41
+ class Battle {
42
+ config;
43
+ log;
44
+ lookup;
45
+ data;
46
+ options;
47
+ buf;
48
+ cache;
49
+ constructor(lookup, data, config) {
50
+ this.config = config;
51
+ this.lookup = lookup;
52
+ this.data = data;
53
+ this.options = new ArrayBuffer(addon.size(0, 'options'));
54
+ this.buf = config.log ? new ArrayBuffer(addon.size(0, 'log')) : undefined;
55
+ this.log = config.log ? new DataView(this.buf) : undefined;
56
+ this.cache = [undefined, undefined];
57
+ }
58
+ update(c1, c2) {
59
+ return addon.update(0, !!this.config.showdown, this.data.buffer, c1, c2, this.buf);
60
+ }
61
+ choices(id, result) {
62
+ return addon.choices(0, !!this.config.showdown, this.data.buffer, id, result[id], this.options);
63
+ }
64
+ choose(id, result, fn) {
65
+ return addon.choose(0, !!this.config.showdown, this.data.buffer, id, result[id], this.options, fn);
66
+ }
67
+ get sides() {
68
+ return this._sides();
69
+ }
70
+ *_sides() {
71
+ yield this.side('p1');
72
+ yield this.side('p2');
73
+ }
74
+ side(id) {
75
+ const i = id === 'p1' ? 0 : 1;
76
+ const side = this.cache[i];
77
+ return side ?? (this.cache[i] = new Side(this, this.lookup, this.data, id));
78
+ }
79
+ foe(side) {
80
+ return this.side(side === 'p1' ? 'p2' : 'p1');
81
+ }
82
+ get turn() {
83
+ return this.data.getUint16(OFFSETS.Battle.turn, data_2.LE);
84
+ }
85
+ get lastDamage() {
86
+ return this.data.getUint16(OFFSETS.Battle.last_damage, data_2.LE);
87
+ }
88
+ get prng() {
89
+ const offset = OFFSETS.Battle.last_selected_indexes + (this.config.showdown ? 4 : 1);
90
+ // Pokémon Showdown's PRNGSeed is always big-endian
91
+ const seed = [0, 0, 0, 0];
92
+ if (this.config.showdown) {
93
+ seed[data_2.LE ? 3 : 0] = this.data.getUint16(offset, data_2.LE);
94
+ seed[data_2.LE ? 2 : 1] = this.data.getUint16(offset + 2, data_2.LE);
95
+ seed[data_2.LE ? 1 : 2] = this.data.getUint16(offset + 4, data_2.LE);
96
+ seed[data_2.LE ? 0 : 3] = this.data.getUint16(offset + 6, data_2.LE);
97
+ }
98
+ else {
99
+ // Gen12RNG seed requires an index, but if we always rotate the seed based on the index when
100
+ // reading we can avoid tracking the index as it will always be zero. NB: the exact bits of
101
+ // the Battle will not roundtrip, but the *logical* Battle state will.
102
+ const index = this.data.getUint8(offset + 10);
103
+ for (let i = 0; i < 10; i++) {
104
+ const j = i - index;
105
+ seed[j < 0 ? j + 10 : j] = this.data.getUint8(offset + i);
106
+ }
107
+ }
108
+ return seed;
109
+ }
110
+ toJSON() {
111
+ return {
112
+ sides: Array.from(this.sides).map(s => s.toJSON()),
113
+ turn: this.turn,
114
+ lastDamage: this.lastDamage,
115
+ prng: this.prng,
116
+ };
117
+ }
118
+ static create(gen, lookup, options) {
119
+ const buf = new ArrayBuffer(SIZES.Battle);
120
+ const data = new DataView(buf);
121
+ Side.init(gen, lookup, options.p1.team, data, OFFSETS.Battle.p1);
122
+ Side.init(gen, lookup, options.p2.team, data, OFFSETS.Battle.p2);
123
+ encodePRNG(data, options.seed);
124
+ return new Battle(lookup, data, options);
125
+ }
126
+ static restore(gen, lookup, battle, options) {
127
+ const buf = new ArrayBuffer(SIZES.Battle);
128
+ const data = new DataView(buf);
129
+ let offset = OFFSETS.Battle.p1;
130
+ const indexes = [];
131
+ for (const side of battle.sides) {
132
+ Side.encode(gen, lookup, battle, side, data, offset);
133
+ offset += SIZES.Side;
134
+ indexes.push(side.lastSelectedIndex ?? 0);
135
+ }
136
+ data.setUint16(OFFSETS.Battle.turn, battle.turn, data_2.LE);
137
+ data.setUint16(OFFSETS.Battle.last_damage, battle.lastDamage, data_2.LE);
138
+ if (options.showdown) {
139
+ data.setUint16(OFFSETS.Battle.last_selected_indexes, indexes[0], data_2.LE);
140
+ data.setUint16(OFFSETS.Battle.last_selected_indexes + 2, indexes[1], data_2.LE);
141
+ }
142
+ else {
143
+ data.setUint8(OFFSETS.Battle.last_selected_indexes, (indexes[1] << 4) | indexes[0]);
144
+ }
145
+ encodePRNG(data, battle.prng);
146
+ return new Battle(lookup, data, options);
147
+ }
148
+ }
149
+ exports.Battle = Battle;
150
+ class Side {
151
+ id;
152
+ battle;
153
+ lookup;
154
+ data;
155
+ offset;
156
+ cache;
157
+ constructor(battle, lookup, data, id) {
158
+ this.id = id;
159
+ this.battle = battle;
160
+ this.lookup = lookup;
161
+ this.data = data;
162
+ this.offset = OFFSETS.Battle[id];
163
+ this.cache = [undefined, undefined, undefined, undefined, undefined, undefined];
164
+ }
165
+ get active() {
166
+ return started(this.data) ? this.get(1) : undefined;
167
+ }
168
+ get pokemon() {
169
+ return this._pokemon();
170
+ }
171
+ *_pokemon() {
172
+ for (let i = 1; i <= 6; i++) {
173
+ const poke = this.get(i);
174
+ if (!poke)
175
+ break;
176
+ yield poke;
177
+ }
178
+ }
179
+ slot(id) {
180
+ for (let slot = 1; slot <= 6; slot++) {
181
+ const i = this.data.getUint8(this.offset + OFFSETS.Side.order + slot - 1);
182
+ if (i === id)
183
+ return slot;
184
+ }
185
+ return undefined;
186
+ }
187
+ get(slot) {
188
+ const id = this.data.getUint8(this.offset + OFFSETS.Side.order + slot - 1) - 1;
189
+ if (id < 0)
190
+ return undefined;
191
+ const poke = this.cache[id];
192
+ if (poke)
193
+ return poke;
194
+ return (this.cache[id] = new Pokemon(this.battle, this.lookup, this.data, this.offset, id));
195
+ }
196
+ get lastUsedMove() {
197
+ const m = this.data.getUint8(this.offset + OFFSETS.Side.last_used_move);
198
+ return m === 0 ? undefined : this.lookup.moveByNum(m);
199
+ }
200
+ get lastSelectedMove() {
201
+ const m = this.data.getUint8(this.offset + OFFSETS.Side.last_selected_move);
202
+ return m === 0 ? undefined : this.lookup.moveByNum(m);
203
+ }
204
+ get lastSelectedIndex() {
205
+ let m;
206
+ if (this.battle.config.showdown) {
207
+ const off = this.id === 'p1' ? 0 : 2;
208
+ m = this.data.getUint16(OFFSETS.Battle.last_selected_indexes + off, data_2.LE);
209
+ }
210
+ else {
211
+ m = this.data.getUint8(OFFSETS.Battle.last_selected_indexes);
212
+ m = this.id === 'p1' ? (m & 0xF) : (m >> 4);
213
+ }
214
+ return m === 0 ? undefined : m;
215
+ }
216
+ toJSON() {
217
+ return {
218
+ active: this.active?.toJSON(),
219
+ pokemon: Array.from(this.pokemon).map(p => p.toJSON()),
220
+ lastSelectedMove: this.lastSelectedMove,
221
+ lastUsedMove: this.lastUsedMove,
222
+ lastSelectedIndex: this.lastSelectedIndex,
223
+ };
224
+ }
225
+ static init(gen, lookup, team, data, offset) {
226
+ const n = Math.min(6, team.length);
227
+ for (let i = 0; i < n; i++) {
228
+ const poke = team[i];
229
+ Pokemon.init(gen, lookup, poke, data, offset, i);
230
+ data.setUint8(offset + OFFSETS.Side.order + i, i + 1);
231
+ }
232
+ }
233
+ static encode(gen, lookup, battle, side, data, offset) {
234
+ let i = 0;
235
+ for (const pokemon of side.pokemon) {
236
+ const off = offset + OFFSETS.Side.pokemon + SIZES.Pokemon * (pokemon.position - 1);
237
+ Pokemon.encodeStored(gen, lookup, pokemon, data, off);
238
+ data.setUint8(offset + OFFSETS.Side.order + (pokemon.position - 1), i + 1);
239
+ i++;
240
+ }
241
+ Pokemon.encodeActive(gen, lookup, battle, side.active, data, offset + OFFSETS.Side.active);
242
+ data.setUint8(offset + OFFSETS.Side.last_selected_move, lookup.moveByID(side.lastSelectedMove));
243
+ data.setUint8(offset + OFFSETS.Side.last_used_move, lookup.moveByID(side.lastUsedMove));
244
+ }
245
+ }
246
+ exports.Side = Side;
247
+ class Pokemon {
248
+ static Volatiles = VOLATILES;
249
+ battle;
250
+ lookup;
251
+ data;
252
+ offset;
253
+ index;
254
+ stored;
255
+ constructor(battle, lookup, data, offset, index) {
256
+ this.battle = battle;
257
+ this.lookup = lookup;
258
+ this.data = data;
259
+ this.offset = {
260
+ order: offset + OFFSETS.Side.order,
261
+ active: offset + OFFSETS.Side.active,
262
+ stored: offset + OFFSETS.Side.pokemon + SIZES.Pokemon * index,
263
+ };
264
+ this.index = index;
265
+ this.stored = new StoredPokemon(this.lookup, this.data, this.offset.stored);
266
+ }
267
+ get position() {
268
+ return this.index + 1;
269
+ }
270
+ get active() {
271
+ return started(this.data) && this.position === this.data.getUint8(this.offset.order);
272
+ }
273
+ stat(stat) {
274
+ if (!this.active)
275
+ return this.stored.stat(stat);
276
+ const off = this.offset.active + OFFSETS.ActivePokemon.stats + OFFSETS.Stats[stat];
277
+ return this.data.getUint16(off, data_2.LE);
278
+ }
279
+ get stats() {
280
+ if (!this.active)
281
+ return this.stored.stats;
282
+ const spc = this.stat('spc');
283
+ return {
284
+ hp: this.stat('hp'),
285
+ atk: this.stat('atk'),
286
+ def: this.stat('def'),
287
+ spe: this.stat('spe'),
288
+ spa: spc,
289
+ spd: spc,
290
+ };
291
+ }
292
+ volatile(bit) {
293
+ if (!this.active)
294
+ return false;
295
+ const off = this.offset.active + OFFSETS.ActivePokemon.volatiles + (bit >> 3);
296
+ const byte = this.data.getUint8(off);
297
+ return !!(byte & (1 << (bit & 7)));
298
+ }
299
+ get volatiles() {
300
+ if (!this.active)
301
+ return {};
302
+ const off = this.offset.active + OFFSETS.ActivePokemon.volatiles;
303
+ const volatiles = {};
304
+ for (const v in Pokemon.Volatiles) {
305
+ if (v === 'Toxic' || v === 'MultiHit')
306
+ continue;
307
+ const volatile = (0, data_1.toID)(v);
308
+ if (this.volatile(Pokemon.Volatiles[v])) {
309
+ if (volatile === 'bide') {
310
+ volatiles[volatile] = {
311
+ duration: (this.data.getUint8(off + (OFFSETS.Volatiles.attacks >> 3)) >> 5) & 0b111,
312
+ damage: this.data.getUint16(off + (OFFSETS.Volatiles.state >> 3), data_2.LE),
313
+ };
314
+ }
315
+ else if (volatile === 'binding') {
316
+ volatiles[volatile] = {
317
+ duration: (this.data.getUint8(off + (OFFSETS.Volatiles.attacks >> 3)) >> 5) & 0b111,
318
+ };
319
+ }
320
+ else if (volatile === 'thrashing') {
321
+ volatiles[volatile] = {
322
+ duration: (this.data.getUint8(off + (OFFSETS.Volatiles.attacks >> 3)) >> 5) & 0b111,
323
+ accuracy: this.data.getUint16(off + (OFFSETS.Volatiles.state >> 3), data_2.LE),
324
+ };
325
+ }
326
+ else if (volatile === 'rage') {
327
+ volatiles[volatile] = {
328
+ accuracy: this.data.getUint16(off + (OFFSETS.Volatiles.state >> 3), data_2.LE),
329
+ };
330
+ }
331
+ else if (volatile === 'confusion') {
332
+ volatiles[volatile] = {
333
+ duration: (this.data.getUint8(off + (OFFSETS.Volatiles.confusion >> 3)) >> 2) & 0b111,
334
+ };
335
+ }
336
+ else if (volatile === 'substitute') {
337
+ volatiles[volatile] = {
338
+ hp: this.data.getUint8(off + (OFFSETS.Volatiles.substitute >> 3)),
339
+ };
340
+ }
341
+ else if (volatile === 'transform') {
342
+ const { player, id } = (0, protocol_1.decodeIdentRaw)((this.data.getUint8(off + (OFFSETS.Volatiles.transform >> 3))) & 0x0F);
343
+ volatiles[volatile] = { player, slot: this.battle.side(player).slot(id) };
344
+ }
345
+ else {
346
+ volatiles[volatile] = {};
347
+ }
348
+ }
349
+ }
350
+ return volatiles;
351
+ }
352
+ move(slot) {
353
+ if (!this.active)
354
+ return this.stored.move(slot);
355
+ const off = this.offset.active + OFFSETS.ActivePokemon.moves;
356
+ const m = this.data.getUint8(off + ((slot - 1) << 1));
357
+ if (m === 0)
358
+ return undefined;
359
+ const id = this.lookup.moveByNum(m);
360
+ const pp = this.data.getUint8(off + ((slot << 1) - 1));
361
+ const byte = this.data.getUint8(this.offset.active +
362
+ OFFSETS.ActivePokemon.volatiles +
363
+ (OFFSETS.Volatiles.disabled_move >> 3));
364
+ const disabled = ((byte & 0b111) === slot)
365
+ ? this.data.getUint8(this.offset.active +
366
+ OFFSETS.ActivePokemon.volatiles +
367
+ (OFFSETS.Volatiles.disabled_duration >> 3)) >> 4
368
+ : undefined;
369
+ return { id, pp, disabled };
370
+ }
371
+ get moves() {
372
+ if (!this.active)
373
+ return this.stored.moves;
374
+ return this._moves();
375
+ }
376
+ *_moves() {
377
+ for (let i = 1; i <= 4; i++) {
378
+ const move = this.move(i);
379
+ if (!move)
380
+ break;
381
+ yield move;
382
+ }
383
+ }
384
+ boost(boost) {
385
+ if (!this.active)
386
+ return 0;
387
+ const b = OFFSETS.Boosts[boost];
388
+ const byte = this.data.getUint8(this.offset.active + OFFSETS.ActivePokemon.boosts + (b >> 3));
389
+ const val = (b & 7) ? byte >> 4 : byte & 0x0F;
390
+ return decodeSigned(val);
391
+ }
392
+ get boosts() {
393
+ const boosts = { atk: 0, def: 0, spe: 0, spa: 0, spd: 0, accuracy: 0, evasion: 0 };
394
+ if (!this.active)
395
+ return boosts;
396
+ for (const b in OFFSETS.Boosts) {
397
+ if (b === 'spc')
398
+ continue;
399
+ const boost = b;
400
+ boosts[boost] = this.boost(boost);
401
+ }
402
+ return boosts;
403
+ }
404
+ get species() {
405
+ if (!this.active)
406
+ return this.stored.species;
407
+ const off = this.offset.active + OFFSETS.ActivePokemon.species;
408
+ return this.lookup.speciesByNum(this.data.getUint8(off));
409
+ }
410
+ get types() {
411
+ if (!this.active)
412
+ return this.stored.types;
413
+ const off = this.offset.active + OFFSETS.ActivePokemon.types;
414
+ return (0, protocol_1.decodeTypes)(this.lookup, this.data.getUint8(off));
415
+ }
416
+ get level() {
417
+ return this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.level);
418
+ }
419
+ get hp() {
420
+ return this.data.getUint16(this.offset.stored + OFFSETS.Pokemon.hp, data_2.LE);
421
+ }
422
+ get status() {
423
+ const status = (0, protocol_1.decodeStatus)(this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.status));
424
+ return status === 'psn' && this.toxic ? 'tox' : status;
425
+ }
426
+ get statusData() {
427
+ return { sleep: this.sleep, self: this.self, toxic: this.toxic };
428
+ }
429
+ get sleep() {
430
+ const val = this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.status);
431
+ return val & 0b111;
432
+ }
433
+ get self() {
434
+ return (this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.status) >> 7) === 1;
435
+ }
436
+ get toxic() {
437
+ if (!this.active)
438
+ return 0;
439
+ const off = this.offset.active + OFFSETS.ActivePokemon.volatiles;
440
+ return this.data.getUint8(off + (OFFSETS.Volatiles.toxic >> 3)) >> 3;
441
+ }
442
+ toJSON() {
443
+ return {
444
+ species: this.species,
445
+ types: this.types,
446
+ level: this.level,
447
+ hp: this.hp,
448
+ status: this.status,
449
+ statusData: this.statusData,
450
+ stats: this.stats,
451
+ boosts: this.boosts,
452
+ moves: Array.from(this.moves),
453
+ volatiles: this.volatiles,
454
+ stored: this.stored.toJSON(),
455
+ position: this.position,
456
+ };
457
+ }
458
+ static init(gen, lookup, set, data, offset, index) {
459
+ const stored = offset + OFFSETS.Side.pokemon + SIZES.Pokemon * index;
460
+ const species = gen.species.get(set.species);
461
+ let hp = 0;
462
+ let off = stored + OFFSETS.Pokemon.stats;
463
+ for (const stat of gen.stats) {
464
+ if (stat === 'spd')
465
+ break;
466
+ const val = gen.stats.calc(stat, species.baseStats[stat], set.ivs?.[stat] ?? 31, set.evs?.[stat] ?? 252, set.level ?? 100);
467
+ data.setUint16(off, val, data_2.LE);
468
+ if (stat === 'hp')
469
+ hp = val;
470
+ off += 2;
471
+ }
472
+ off = stored + OFFSETS.Pokemon.moves;
473
+ for (const m of set.moves) {
474
+ const move = gen.moves.get(m);
475
+ data.setUint8(off++, move.num);
476
+ data.setUint8(off++, Math.min(move.pp / 5 * 8, gen.num === 1 ? 61 : 64));
477
+ }
478
+ data.setUint16(stored + OFFSETS.Pokemon.hp, hp, data_2.LE);
479
+ data.setUint8(stored + OFFSETS.Pokemon.species, species.num);
480
+ data.setUint8(stored + OFFSETS.Pokemon.types, encodeTypes(lookup, species.types));
481
+ data.setUint8(stored + OFFSETS.Pokemon.level, set.level ?? 100);
482
+ }
483
+ static encodeStored(gen, lookup, pokemon, data, offset) {
484
+ let off = 0;
485
+ const stats = pokemon.stored.stats;
486
+ for (const stat of gen.stats) {
487
+ if (stat === 'spd')
488
+ break;
489
+ data.setUint16(offset + OFFSETS.Pokemon.stats + off, stats[stat], data_2.LE);
490
+ off += 2;
491
+ }
492
+ off = offset + OFFSETS.Pokemon.moves;
493
+ for (const ms of pokemon.stored.moves) {
494
+ data.setUint8(off++, lookup.moveByID(ms.id));
495
+ data.setUint8(off++, ms.pp);
496
+ }
497
+ data.setUint16(offset + OFFSETS.Pokemon.hp, pokemon.hp, data_2.LE);
498
+ const species = gen.species.get(pokemon.stored.species);
499
+ data.setUint8(offset + OFFSETS.Pokemon.species, species.num);
500
+ data.setUint8(offset + OFFSETS.Pokemon.types, encodeTypes(lookup, species.types));
501
+ data.setUint8(offset + OFFSETS.Pokemon.level, pokemon.level);
502
+ data.setUint8(offset + OFFSETS.Pokemon.status, encodeStatus(pokemon));
503
+ }
504
+ static encodeActive(gen, lookup, battle, pokemon, data, offset) {
505
+ if (!pokemon)
506
+ return;
507
+ let off = 0;
508
+ const stats = pokemon.stats;
509
+ for (const stat of gen.stats) {
510
+ if (stat === 'spd')
511
+ break;
512
+ data.setUint16(offset + OFFSETS.ActivePokemon.stats + off, stats[stat], data_2.LE);
513
+ off += 2;
514
+ }
515
+ off = offset + OFFSETS.ActivePokemon.moves;
516
+ let slot = 1;
517
+ const disabled = { duration: 0, move: 0 };
518
+ for (const ms of pokemon.moves) {
519
+ data.setUint8(off++, lookup.moveByID(ms.id));
520
+ data.setUint8(off++, ms.pp);
521
+ if (ms.disabled !== undefined) {
522
+ disabled.duration = ms.disabled;
523
+ disabled.move = slot;
524
+ }
525
+ slot++;
526
+ }
527
+ off = offset + OFFSETS.ActivePokemon.volatiles;
528
+ const volatiles = pokemon.volatiles;
529
+ data.setUint8(off + (Pokemon.Volatiles.Bide >> 3), +!!volatiles.bide | (+!!volatiles.thrashing << 1) | (+!!volatiles.flinch << 3) |
530
+ (+!!volatiles.charging << 4) | (+!!volatiles.binding << 5) |
531
+ (+!!volatiles.invulnerable << 6) | (+!!volatiles.confusion << 7));
532
+ data.setUint8(off + (Pokemon.Volatiles.Mist >> 3), +!!volatiles.mist | (+!!volatiles.focusenergy << 1) |
533
+ (+!!volatiles.substitute << 2) | (+!!volatiles.recharging << 3) |
534
+ (+!!volatiles.rage << 4) | (+!!volatiles.leechseed << 5) |
535
+ (+!!pokemon.statusData.toxic << 6) | (+!!volatiles.lightscreen << 7));
536
+ const confusion = volatiles.confusion?.duration ?? 0;
537
+ const attacks = volatiles.binding?.duration ??
538
+ volatiles.thrashing?.duration ?? 0;
539
+ data.setUint8(off + (Pokemon.Volatiles.Reflect >> 3), +!!volatiles.reflect | (+!!volatiles.transform << 1) |
540
+ (confusion << 2) | (attacks << 5));
541
+ const state = volatiles.bide?.damage ??
542
+ volatiles.thrashing?.accuracy ??
543
+ volatiles.rage?.accuracy ?? 0;
544
+ data.setUint16(off + (OFFSETS.Volatiles.state >> 3), state, data_2.LE);
545
+ data.setUint8(off + (OFFSETS.Volatiles.substitute >> 3), volatiles.substitute?.hp ?? 0);
546
+ let transform = 0;
547
+ if (volatiles.transform) {
548
+ const side = Array.from(battle.sides)[volatiles.transform.player === 'p1' ? 0 : 1];
549
+ const id = Array.from(side.pokemon)[volatiles.transform.slot - 1].position;
550
+ transform = (+!!(volatiles.transform.player === 'p2') << 3) | id;
551
+ }
552
+ data.setUint8(off + (OFFSETS.Volatiles.transform >> 3), (transform | (disabled.duration << 4)));
553
+ data.setUint8(off + (OFFSETS.Volatiles.toxic >> 3), (disabled.move | ((pokemon.statusData.toxic ?? 0) << 3)));
554
+ off = offset + OFFSETS.ActivePokemon.boosts;
555
+ const boosts = pokemon.boosts;
556
+ data.setUint8(off++, encodeSigned(boosts.atk) | (encodeSigned(boosts.def) << 4));
557
+ data.setUint8(off++, encodeSigned(boosts.spe) | (encodeSigned(boosts.spa) << 4));
558
+ data.setUint8(off++, encodeSigned(boosts.accuracy) | (encodeSigned(boosts.evasion) << 4));
559
+ data.setUint8(offset + OFFSETS.ActivePokemon.species, lookup.speciesByID(pokemon.species));
560
+ data.setUint8(offset + OFFSETS.ActivePokemon.types, encodeTypes(lookup, pokemon.types));
561
+ }
562
+ }
563
+ exports.Pokemon = Pokemon;
564
+ class StoredPokemon {
565
+ lookup;
566
+ data;
567
+ offset;
568
+ constructor(lookup, data, offset) {
569
+ this.lookup = lookup;
570
+ this.data = data;
571
+ this.offset = offset;
572
+ }
573
+ stat(stat) {
574
+ return this.data.getUint16(this.offset + OFFSETS.Pokemon.stats + OFFSETS.Stats[stat], data_2.LE);
575
+ }
576
+ get stats() {
577
+ const spc = this.stat('spc');
578
+ return {
579
+ hp: this.stat('hp'),
580
+ atk: this.stat('atk'),
581
+ def: this.stat('def'),
582
+ spe: this.stat('spe'),
583
+ spa: spc,
584
+ spd: spc,
585
+ };
586
+ }
587
+ move(slot) {
588
+ const m = this.data.getUint8(this.offset + OFFSETS.Pokemon.moves + ((slot - 1) << 1));
589
+ if (m === 0)
590
+ return undefined;
591
+ const id = this.lookup.moveByNum(m);
592
+ const pp = this.data.getUint8(this.offset + OFFSETS.Pokemon.moves + ((slot << 1) - 1));
593
+ return { id, pp };
594
+ }
595
+ get moves() {
596
+ return this._moves();
597
+ }
598
+ *_moves() {
599
+ for (let i = 1; i <= 4; i++) {
600
+ const move = this.move(i);
601
+ if (!move)
602
+ break;
603
+ yield move;
604
+ }
605
+ }
606
+ get species() {
607
+ return this.lookup.speciesByNum(this.data.getUint8(this.offset + OFFSETS.Pokemon.species));
608
+ }
609
+ get types() {
610
+ return (0, protocol_1.decodeTypes)(this.lookup, this.data.getUint8(this.offset + OFFSETS.Pokemon.types));
611
+ }
612
+ toJSON() {
613
+ return {
614
+ species: this.species,
615
+ types: this.types,
616
+ stats: this.stats,
617
+ moves: Array.from(this.moves),
618
+ };
619
+ }
620
+ }
621
+ exports.StoredPokemon = StoredPokemon;
622
+ function encodePRNG(data, seed) {
623
+ const offset = OFFSETS.Battle.last_damage + 2 + (seed.length === 4 ? 4 : 1);
624
+ if (seed.length === 4) {
625
+ data.setUint16(offset, seed[data_2.LE ? 3 : 0], data_2.LE);
626
+ data.setUint16(offset + 2, seed[data_2.LE ? 2 : 1], data_2.LE);
627
+ data.setUint16(offset + 4, seed[data_2.LE ? 1 : 2], data_2.LE);
628
+ data.setUint16(offset + 6, seed[data_2.LE ? 0 : 3], data_2.LE);
629
+ }
630
+ else {
631
+ for (let i = 0; i < 10; i++) {
632
+ data.setUint8(offset + i, seed[i]);
633
+ }
634
+ // NB: ArrayBuffer is zero initialized already so we don't need to set the index to zero
635
+ }
636
+ }
637
+ function started(data) {
638
+ return data.getUint16(OFFSETS.Battle.turn, data_2.LE) !== 0;
639
+ }
640
+ function encodeTypes(lookup, types) {
641
+ return (lookup.typeByName(types[1] ?? types[0]) << 4) | lookup.typeByName(types[0]);
642
+ }
643
+ function decodeSigned(n) {
644
+ return (n > 7) ? -(0b10000 - n) : n;
645
+ }
646
+ function encodeSigned(n) {
647
+ return (n < 0) ? 0b10000 + n : n;
648
+ }
649
+ function encodeStatus(pokemon) {
650
+ if (pokemon.statusData.sleep) {
651
+ if (pokemon.status !== 'slp') {
652
+ throw new Error('Pokemon is not asleep but has non-zero sleep turns');
653
+ }
654
+ return (pokemon.statusData.self ? 0x80 : 0) | pokemon.statusData.sleep;
655
+ }
656
+ else if (pokemon.statusData.self) {
657
+ throw new Error('Pokemon is not asleep but has self-inflicted sleep bit set');
658
+ }
659
+ switch (pokemon.status) {
660
+ case 'tox':
661
+ case 'psn': return 1 << 3;
662
+ case 'brn': return 1 << 4;
663
+ case 'frz': return 1 << 5;
664
+ case 'par': return 1 << 6;
665
+ }
666
+ return 0;
667
+ }
668
+ //# sourceMappingURL=gen1.js.map