@pkmn/engine 0.1.0-dev.fb5bf955 → 0.1.0-dev.fc150752

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 (49) hide show
  1. package/README.md +71 -60
  2. package/build/pkg/addon.d.ts +2 -2
  3. package/build/pkg/addon.js +8 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data/layout.json +25 -3
  6. package/build/pkg/gen1.d.ts +5 -4
  7. package/build/pkg/gen1.js +47 -31
  8. package/build/pkg/gen1.js.map +1 -1
  9. package/build/pkg/index.d.ts +15 -12
  10. package/build/pkg/index.js.map +1 -1
  11. package/build/pkg/protocol.d.ts +4 -2
  12. package/build/pkg/protocol.js +4 -2
  13. package/build/pkg/protocol.js.map +1 -1
  14. package/build/test/display.js +31 -13
  15. package/build/test/display.js.map +1 -1
  16. package/build.zig +95 -76
  17. package/package.json +26 -16
  18. package/src/bin/install-pkmn-engine +11 -6
  19. package/src/bin/pkmn-debug +29 -6
  20. package/src/lib/binding/node.zig +46 -141
  21. package/src/lib/binding/wasm.zig +6 -4
  22. package/src/lib/common/battle.zig +17 -0
  23. package/src/lib/common/data.zig +14 -26
  24. package/src/lib/common/debug.zig +21 -11
  25. package/src/lib/common/js.zig +118 -0
  26. package/src/lib/common/optional.zig +60 -0
  27. package/src/lib/common/options.zig +12 -3
  28. package/src/lib/common/protocol.zig +315 -197
  29. package/src/lib/common/rational.zig +353 -0
  30. package/src/lib/common/rng.zig +11 -11
  31. package/src/lib/common/util.zig +12 -0
  32. package/src/lib/gen1/README.md +265 -155
  33. package/src/lib/gen1/calc.zig +426 -0
  34. package/src/lib/gen1/chance.zig +1036 -0
  35. package/src/lib/gen1/data/moves.zig +178 -180
  36. package/src/lib/gen1/data/species.zig +2 -2
  37. package/src/lib/gen1/data/types.zig +3 -3
  38. package/src/lib/gen1/data.zig +73 -59
  39. package/src/lib/gen1/helpers.zig +592 -32
  40. package/src/lib/gen1/mechanics.zig +994 -641
  41. package/src/lib/gen2/README.md +87 -13
  42. package/src/lib/gen2/data/items.zig +3 -3
  43. package/src/lib/gen2/data/moves.zig +635 -563
  44. package/src/lib/gen2/data/species.zig +1 -1
  45. package/src/lib/gen2/data/types.zig +3 -3
  46. package/src/lib/gen2/data.zig +104 -74
  47. package/src/lib/gen2/helpers.zig +16 -22
  48. package/src/lib/gen2/mechanics.zig +916 -11
  49. package/src/lib/pkmn.zig +68 -9
package/README.md CHANGED
@@ -12,13 +12,21 @@
12
12
  <img alt="License" src="https://img.shields.io/badge/License-MIT-blue.svg" />
13
13
  </a>
14
14
  </p>
15
+
16
+ > [!WARNING]
17
+ > **This project is under heavy development** and currently the `main` branch contains numerous breaking
18
+ > changes which may not work and which are not fully documented. Please **wait for the forthcoming
19
+ > initial v0.1 release** before depending on this project (code from the
20
+ > [`old`](https://github.com/pkmn/engine/releases/tag/old) tag can also be used to experiment with a
21
+ > more stable version of the codebase).
22
+
15
23
  <hr />
16
24
 
17
25
  A minimal, complete, Pokémon battle simulation engine optimized for performance and
18
26
  [designed](docs/DESIGN.md) for tooling, embedded systems, and [artificial
19
27
  intelligence](https://github.com/pkmn/0-ERROR) use cases. This engine aims to be a frame-accurate and
20
28
  [bug-for-bug compatible](http://www.catb.org/jargon/html/B/bug-for-bug-compatible.html)
21
- implementation of both Pokémon battles as defined by the original game code, and the [Pokémon
29
+ implementation of both Pokémon battles as defined by the original game code and the [Pokémon
22
30
  Showdown](https://pokemonshowdown.com/)[^1] simulator which represents Pokémon battling as practically
23
31
  interpreted online.
24
32
 
@@ -49,14 +57,14 @@ $ zig build --prefix /usr/local -Doptimize=ReleaseFast
49
57
  ```
50
58
 
51
59
  The Zig website has [installation instructions](https://ziglang.org/learn/getting-started/) which
52
- walk through how to install Zig on each platform - the engine code should work on Zig v0.11.0 dev
53
- build 2168 or greater, though tracks Zig's master branch so this may change in the future if
54
- breaking language changes are introduced:
60
+ walk through how to install Zig on each platform - the engine code should work on Zig v0.11.0 or
61
+ greater, though tracks Zig's master branch so this may change in the future if breaking language
62
+ changes are introduced.
55
63
 
56
64
  `libpkmn` can be built with `-Dshowdown` to instead produce the Pokémon Showdown compatible
57
- `libpkmn-showdown` library. Furthermore, trace logging can be enabled through `-Dtrace`. The
58
- `libpkmn` and `libpkmn-showdown` objects available in the binary release are compiled with and
59
- without the `-Dtrace` flag respectively.
65
+ `libpkmn-showdown` library. Furthermore, protocol message logging can be enabled through `-Dlog`.
66
+ The `libpkmn` and `libpkmn-showdown` objects available in the binary release are both compiled with
67
+ the `-Dlog` flag by default.
60
68
 
61
69
  ### `@pkmn/engine`
62
70
 
@@ -67,12 +75,12 @@ $ npm install @pkmn/engine
67
75
  ```
68
76
 
69
77
  The driver depends on being able to find compiled Node/WASM addons in
70
- `node_modules/@pkmn/engine/build/lib` in order to be useful. When you install the package a
71
- [`postinstall` lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) will run
72
- [`install-pkmn-engine`](src/bin/install-pkmn-engine) which will check for a compatible `zig`
73
- compiler (see above regarding minimum version) and download one to
74
- `node_module/@pkmn/engine/build/bin` if it can't find one, as well as looking for (and downloading,
75
- if necessary) the required Node headers needed to successfully build the addons natively.
78
+ `node_modules/@pkmn/engine/build/lib` to be useful. When you install the package a [`postinstall`
79
+ lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) runs
80
+ [`install-pkmn-engine`](src/bin/install-pkmn-engine) which checks for a compatible `zig` compiler
81
+ (see earlier regarding minimum version) and download one to `node_module/@pkmn/engine/build/bin` if
82
+ it can't find one, as well as looking for (and downloading, if necessary) the required Node headers
83
+ needed to successfully build the addons natively.
76
84
 
77
85
  **If you have configured `npm` to `--ignore-scripts` you must either run `npx install-pkmn-engine`
78
86
  directly or build the addons manually and place the artifacts in the expected paths.**
@@ -88,23 +96,23 @@ adding the following to your `build.zig`:
88
96
  const std = @import("std");
89
97
  const pkmn = @import("lib/pkmn/build.zig");
90
98
 
91
- pub fn build(b: *std.build.Builder) void {
99
+ pub fn build(b: *std.Build) void {
92
100
  ...
93
- exe.addModule("pkmn", pkmn.module(b, .{ .showdown = true, .trace = true }));
101
+ exe.addModule("pkmn", pkmn.module(b, .{ .showdown = true, .log = true }));
94
102
  ...
95
103
  }
96
104
  ```
97
105
 
98
106
  The engine's `build.zig` exposes a `module` function that takes an options structure to allow for
99
- configuring whether or not Pokémon Showdown compatibility mode or trace logs should be enabled.
100
- Alternatively, you may set options via a `pkmn_options` [root source file
107
+ configuring whether or not Pokémon Showdown compatibility mode or protocol message logging should be
108
+ enabled. Alternatively, you may set options via a `pkmn_options` [root source file
101
109
  declaration](https://ziglang.org/documentation/master/#Root-Source-File). There are several
102
110
  undocumented internal options that can be tweaked as well via build or root options, though these
103
- options are not officially supported, affect correctness, and may change meaning or behavior without
111
+ options aren't officially supported, affect correctness, and may change meaning or behavior without
104
112
  warning. Use at your own risk.
105
113
 
106
114
  ```zig
107
- pub const pkmn_options = .{ .showdown = true, .trace = true };
115
+ pub const pkmn_options = .{ .showdown = true, .log = true };
108
116
  ```
109
117
 
110
118
  ## Usage
@@ -112,23 +120,23 @@ pub const pkmn_options = .{ .showdown = true, .trace = true };
112
120
  For each Pokémon generation, the engine provides a battle structure with two functions - a function
113
121
  to `update` the battle's state based on both players' choices and a function that defines which
114
122
  `choices` are valid at that decision point (at the beginning of a battle both player's must "pass"
115
- as their first choice in order to switch in the first member of their party). If `-Dtrace` logging
116
- is enabled each update will produce logs which can be decoded into a buffer (`LOGS_SIZE` constants
117
- are provided to make it easy to allocate a buffer of the correct size). Each `update` will return a
118
- result to indicate either that the battle has terminated or which types of choices will be available
119
- for each player.
123
+ as their first choice to switch in the first member of their party). If `-Dlog` protocol logging is
124
+ enabled each update produces logs which can be decoded into a buffer (`LOGS_SIZE` constants are
125
+ provided to make it easy to allocate a buffer of the correct size). Each `update` returns a result
126
+ to indicate either that the battle has terminated or which types of choices are available for each
127
+ player.
120
128
 
121
129
  Unlike [Pokémon Showdown's
122
130
  SIM-PROTOCOL](https://github.com/smogon/pokemon-showdown/blob/master/sim/SIM-PROTOCOL.md#choice-requests)
123
131
  which provides a rich request object at each decision point, the pkmn engine computes the possible
124
- choices on demand based on the result of the previous `update` and the battle state (`OPTIONS_SIZE`
132
+ choices on demand based on the result of the previous `update` and the battle state (`CHOICES_SIZE`
125
133
  is a good size to initialize the buffer passed to `choices`). **Attempting to update the battle with
126
134
  a choice not present in the options returned by `choices` is undefined behavior and may corrupt
127
135
  state or cause the engine to crash.**
128
136
 
129
137
  Battles may be played out from any point but to freshly initialize a battle which is yet to start
130
138
  the turn count and active Pokémon should be zeroed out (driver code should handle this for you).
131
- Most driver code should also provide helpers to make initialize a battle convenient - check the
139
+ Most driver code should also provide helpers to make initializing battle convenient - check the
132
140
  respective documentation and examples for the bindings being used. The engine's [protocol
133
141
  documentation](docs/PROTOCOL.md) goes into greater detail on the specifics of updates and the
134
142
  potential logs that may result.
@@ -139,36 +147,39 @@ potential logs that may result.
139
147
  ### C
140
148
 
141
149
  [`pkmn.h`](src/include/pkmn.h) exports the C API for `libpkmn`. Symbols are all prefixed with
142
- `pkmn_` to avoid name collisions. If `-Dtrace` is enabled and logging throws an error then the error
143
- will be encoded in the `pkmn_result` and can be checked with `pkmn_error`.
150
+ `pkmn_` to avoid name collisions. If `-Dlog` is enabled and logging throws an error then the error
151
+ is encoded in the `pkmn_result` and can be checked with `pkmn_error`.
144
152
 
145
153
  ```c
146
154
  #include <pkmn.h>
147
155
 
156
+ pkmn_battle_options options;
157
+ pkmn_gen1_battle_options_set(&options, ...);
148
158
  pkmn_battle battle = ...;
149
- uint8_t buf[PKMN_LOGS_SIZE];
159
+
150
160
  pkmn_result result;
151
161
  pkmn_choice c1 = 0, c2 = 0;
152
- while (!pkmn_result_type(result = pkmn_battle_update(&battle, c1, c2, buf, PKMN_LOGS_SIZE))) {
162
+ while (!pkmn_result_type(result = pkmn_battle_update(&battle, c1, c2, &options))) {
153
163
  c1 = choose(PKMN_PLAYER_P1, pkmn_result_p1(result));
154
164
  c2 = choose(PKMN_PLAYER_P2, pkmn_result_p2(result));
165
+ pkmn_gen1_battle_options_set(&options, ...);
155
166
  }
156
167
  if (pkmn_error(result)) exit(1);
157
168
  ```
158
169
 
159
170
  [(full code)](src/examples/c/example.c)
160
171
 
161
- The C API does not export any helpers for creating or accessing the opaque battle objects - it is
172
+ The C API doesn't export any helpers for creating or accessing the opaque battle objects - it's
162
173
  instead intended to be used as the foundation for more ergonomic bindings in [other
163
174
  languages](#other) (the lack of namespaces and bit fields having an implementation-defined layout in
164
175
  C are the main contributing factors to the sparseness of what `libpkmn` chooses to expose).
165
176
 
166
177
  ### JavaScript / TypeScript
167
178
 
168
- `@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) which
179
+ `@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) package which
169
180
  requires a `Dex` implementation to be provided as well. The `Battle.create` function can be used to
170
181
  initialize a `Battle` from the beginning, or `Battle.restore` can be used to re-instantiate a battle
171
- which is in already progress. If logging is enabled the output can be turned into Pokémon Showdown
182
+ which is already in progress. If logging is enabled the output can be turned into Pokémon Showdown
172
183
  protocol via `Log.parse`.
173
184
 
174
185
  ```ts
@@ -193,18 +204,18 @@ console.log(result);
193
204
 
194
205
  [(full code)](src/examples/js/example.ts)
195
206
 
196
- By default, the `@pkmn/engine` package will compile the engine with `-Dshowdown`, though by running
207
+ By default, the `@pkmn/engine` package compiles the engine with `-Dshowdown`, though by running
197
208
  `install-pkmn-engine` directly and passing in `--options` you can override this default and build
198
209
  different configurations of the extension for the driver to use. The driver can support
199
210
  configurations both with and without Pokémon Showdown compatibility simultaneously if present. On
200
- update, the post-install script will attempt to rebuild whichever extensions it finds with the same
211
+ update, the post-install script attempts to rebuild whichever extensions it finds with the same
201
212
  configuration parameters that were originally used meaning updating to the newest version of the
202
213
  library should be seamless.
203
214
 
204
215
  Despite relying on the native engine code, the `@pkmn/engine` code is designed to also work in
205
216
  browsers which support [WebAssembly](https://webassembly.org/). Running `npm run start:web` from the
206
- [`examples`](src/examples/js) directory will start a server that can be used to demonstrate the
207
- engine running in the browser.
217
+ [`examples`](src/examples/js) directory starts a server that can be used to demonstrate the engine
218
+ running in the browser.
208
219
 
209
220
  ### Zig
210
221
 
@@ -216,18 +227,18 @@ const std = @import("std");
216
227
  const pkmn = @import("pkmn");
217
228
 
218
229
  var random = std.rand.DefaultPrng.init(seed).random();
219
- var options: [pkmn.OPTIONS_SIZE]pkmn.Choice = undefined;
230
+ var choices: [pkmn.CHOICES_SIZE]pkmn.Choice = undefined;
220
231
 
221
232
  var battle = ...
222
- var log = ...
233
+ var options = pkmn.battle.options(...);
223
234
 
224
235
  var c1 = pkmn.Choice{};
225
236
  var c2 = pkmn.Choice{};
226
237
 
227
- var result = try battle.update(c1, c2, log);
228
- while (result.type == .None) : (result = try battle.update(c1, c2, log)) {
229
- c1 = options[random.uintLessThan(u8, battle.choices(.P1, result.p1, &options))];
230
- c2 = options[random.uintLessThan(u8, battle.choices(.P2, result.p2, &options))];
238
+ var result = try battle.update(c1, c2, &options);
239
+ while (result.type == .None) : (result = try battle.update(c1, c2, &options)) {
240
+ c1 = choices[random.uintLessThan(u8, battle.choices(.P1, result.p1, &choices))];
241
+ c2 = choices[random.uintLessThan(u8, battle.choices(.P2, result.p2, &choices))];
231
242
  }
232
243
 
233
244
  std.debug.print("{}\n", .{result.type});
@@ -237,7 +248,7 @@ std.debug.print("{}\n", .{result.type});
237
248
 
238
249
  The Zig package also supports some APIs which are difficult to expose elsewhere such as the
239
250
  `FixedRNG` which allows you to fully specify the exact RNG frames (which can be useful for ensuring
240
- certain outcomes/effects always occur) for a battle, though doing so will change the size of the
251
+ certain outcomes/effects always occur) for a battle, though doing so changes the size of the
241
252
  `Battle` object.
242
253
 
243
254
  ### Other
@@ -248,12 +259,12 @@ simplify the process even further [`src/data`](src/data) contains JSON dumps of
248
259
  data, structure sizes and offsets, and protocol information used by the reference driver code. The
249
260
  following is a list of known `libpkmn` bindings written by developers outside of the [pkmn
250
261
  organization](https://github.com/pkmn) that may be helpful (though note that these projects may not
251
- necessarily be up-to-date/complete/correct - inclusion in this list does not imply endorsement):
262
+ necessarily be up-to-date/complete/correct - inclusion in this list doesn't imply endorsement):
252
263
 
253
- | Language | License | URL |
254
- | ---------- | --------- | --------------------------------------- |
255
- | **C++** | `BSL-1.0` | https://github.com/pasyg/engine-wrapper |
256
- | **Python** | `MIT` | https://github.com/AnnikaCodes/PyKMN |
264
+ | Language | License | URL |
265
+ | ---------- | --------- | ------------------------------------ |
266
+ | **C++** | `BSL-1.0` | https://github.com/pasyg/wrapsire |
267
+ | **Python** | `MIT` | https://github.com/AnnikaCodes/PyKMN |
257
268
 
258
269
 
259
270
  #### `pkmn-debug`
@@ -276,7 +287,7 @@ By default, `pkmn-debug` expects to read [binary debug protocol](docs/PROTOCOL.m
276
287
  $ <cmd> | pkmn-debug > index.html
277
288
  ```
278
289
 
279
- Alternatively, if a single filename argument is passed to the `pkmn-debug` it will read from that
290
+ Alternatively, if a single filename argument is passed to the `pkmn-debug` it reads from that
280
291
  instead:
281
292
 
282
293
  ```sh
@@ -298,14 +309,14 @@ Currently, most of the foundational work from stage 0 is done:
298
309
 
299
310
  - [benchmark and integration testing](src/test) infrastructure
300
311
  - [documentation](docs) about design, research, methodology, etc
301
- - definition and implementation of the [protocol](docs/PROTOCOL.md) that will be used by the engine
312
+ - definition and implementation of the [protocol](docs/PROTOCOL.md) that's used by the engine
302
313
 
303
- **Stage 1 is currently in progress** and will see the implementation of the actual Generation I & II
314
+ **Stage 1 is currently in progress** and involves the implementation of the actual Generation I & II
304
315
  battle engines, followed by Generation III & IV in stage 2. The implementation of further Pokémon
305
- generations is in scope for the project but should not be considered as part of the immediate
306
- roadmap (i.e. exploring the options for broadening support for old generation APIs will be given
307
- higher priority than implementing more modern generations). Furthermore, implementation of modern
308
- generations is soft-blocked on the [availability of high quality
316
+ generations is in scope for the project but shouldn't be considered part of the immediate roadmap
317
+ (i.e. exploring the options for broadening support for old generation APIs is given higher priority
318
+ than implementing more modern generations). Furthermore, implementation of modern generations is
319
+ soft-blocked on the [availability of high quality
309
320
  decompilations](https://github.com/orgs/pret/repositories) of the original games in question.
310
321
 
311
322
  Certain features will always be deemed **out of scope**:
@@ -323,10 +334,10 @@ The pkmn engine is distributed under the terms of the [MIT License](LICENSE).
323
334
  [^1]: In the case of Pokémon Showdown, only bugs which stem from a misimplementation of specific
324
335
  effects are reproduced in the engine, bugs which are the result of a misunderstanding of the
325
336
  fundamental mechanics of Pokémon or which simply arise due to specific Pokémon Showdown
326
- implementation details that are not replicable without making the same (incorrect) architectural
327
- choices are not. Furthermore, the "Pokémon Showdown" code referenced by this project includes
337
+ implementation details that aren't replicable without making the same (incorrect) architectural
338
+ choices aren't. Furthermore, the "Pokémon Showdown" code referenced by this project includes
328
339
  several [patches](docs/TESTING.md#patches) to improve accuracy and smooth over some of the more
329
340
  egregious implementation issues. In practical terms, the vast majority of games played out in the
330
- pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator will be the same,
331
- it is only in a well defined and documented set of circumstances where the two implementations
341
+ pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator are the same,
342
+ it's only in a well defined and documented set of circumstances where the two implementations
332
343
  diverge.
@@ -1,7 +1,7 @@
1
1
  import { Choice, Player, Result } from '.';
2
2
  export declare function check(showdown: boolean): void;
3
- export declare function supports(showdown: boolean, trace?: boolean): boolean;
3
+ export declare function supports(showdown: boolean, log?: boolean): boolean;
4
4
  export declare function update(index: number, showdown: boolean, battle: ArrayBuffer, c1?: Choice, c2?: Choice, log?: ArrayBuffer): Result;
5
5
  export declare function choices(index: number, showdown: boolean, battle: ArrayBuffer, player: Player, choice: Choice['type'], buf: ArrayBuffer): Choice[];
6
6
  export declare function choose(index: number, showdown: boolean, battle: ArrayBuffer, player: Player, choice: Choice['type'], buf: ArrayBuffer, fn: (n: number) => number): Choice;
7
- export declare function size(index: number, type: 'options' | 'log'): number;
7
+ export declare function size(index: number, type: 'choices' | 'log'): number;
@@ -34,11 +34,12 @@ function load() {
34
34
  let pkmn = undefined;
35
35
  let showdown = undefined;
36
36
  try {
37
- pkmn = require(path.join(ROOT, 'build', 'lib', 'pkmn.node'));
37
+ pkmn = require(path.join(ROOT, 'build', 'lib', 'pkmn.node')).engine;
38
38
  }
39
39
  catch { }
40
40
  try {
41
- showdown = require(path.join(ROOT, 'build', 'lib', 'pkmn-showdown.node'));
41
+ showdown =
42
+ require(path.join(ROOT, 'build', 'lib', 'pkmn-showdown.node')).engine;
42
43
  }
43
44
  catch { }
44
45
  if (!pkmn && !showdown) {
@@ -48,7 +49,7 @@ function load() {
48
49
  }
49
50
  function check(showdown) {
50
51
  if (!load()[+showdown]) {
51
- const opts = ADDON[+!showdown].options.trace ? ['-Dtrace'] : [];
52
+ const opts = ADDON[+!showdown].options.log ? ['-log'] : [];
52
53
  if (showdown)
53
54
  opts.push('-Dshowdown');
54
55
  throw new Error(`@pkmn/engine has ${showdown ? 'not' : 'only'} been configured to support Pokémon Showdown.` +
@@ -56,12 +57,12 @@ function check(showdown) {
56
57
  }
57
58
  }
58
59
  exports.check = check;
59
- function supports(showdown, trace) {
60
+ function supports(showdown, log) {
60
61
  if (!load()[+showdown])
61
62
  return false;
62
- if (trace === undefined)
63
+ if (log === undefined)
63
64
  return true;
64
- return ADDON[+showdown].options.trace === trace;
65
+ return ADDON[+showdown].options.log === log;
65
66
  }
66
67
  exports.supports = supports;
67
68
  function update(index, showdown, battle, c1, c2, log) {
@@ -105,7 +106,7 @@ function choose(index, showdown, battle, player, choice, buf, fn) {
105
106
  exports.choose = choose;
106
107
  function size(index, type) {
107
108
  const bindings = (ADDON[1] ?? ADDON[0]).bindings[index];
108
- return type[0] === 'o' ? bindings.OPTIONS_SIZE : bindings.LOGS_SIZE;
109
+ return type[0] === 'c' ? bindings.CHOICES_SIZE : bindings.LOGS_SIZE;
109
110
  }
110
111
  exports.size = size;
111
112
  //# sourceMappingURL=addon.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"addon.js","sourceRoot":"","sources":["../../src/pkg/addon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"addon.js","sourceRoot":"","sources":["../../src/pkg/addon.ts"],"names":[],"mappings":"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"}
@@ -4,15 +4,17 @@
4
4
  "Battle": 384,
5
5
  "Side": 184,
6
6
  "Pokemon": 24,
7
- "ActivePokemon": 32
7
+ "ActivePokemon": 32,
8
+ "Actions": 16,
9
+ "Summaries": 12
8
10
  },
9
11
  "offsets": {
10
12
  "Battle": {
11
13
  "sides": 0,
12
14
  "turn": 368,
13
15
  "last_damage": 370,
14
- "last_selected_indexes": 372,
15
- "rng": 373
16
+ "last_moves": 372,
17
+ "rng": 374
16
18
  },
17
19
  "Side": {
18
20
  "pokemon": 0,
@@ -80,6 +82,26 @@
80
82
  "disabled_duration": 52,
81
83
  "disabled_move": 56,
82
84
  "toxic": 59
85
+ },
86
+ "Action": {
87
+ "damage": 0,
88
+ "hit": 8,
89
+ "critical_hit": 10,
90
+ "secondary_chance": 12,
91
+ "speed_tie": 14,
92
+ "confused": 16,
93
+ "paralyzed": 18,
94
+ "duration": 20,
95
+ "durations": 24,
96
+ "move_slot": 40,
97
+ "multi_hit": 44,
98
+ "psywave": 48,
99
+ "metronome": 56
100
+ },
101
+ "Damage": {
102
+ "base": 0,
103
+ "final": 2,
104
+ "capped": 4
83
105
  }
84
106
  }
85
107
  }
@@ -39,10 +39,11 @@ export declare class Side implements Gen1.Side {
39
39
  get(slot: Slot): Pokemon | undefined;
40
40
  get lastUsedMove(): ID | undefined;
41
41
  get lastSelectedMove(): ID | undefined;
42
- get lastSelectedIndex(): 1 | 2 | 3 | 4 | undefined;
42
+ get lastMoveIndex(): 1 | 2 | 3 | 4 | undefined;
43
+ get lastMoveCounterable(): boolean;
43
44
  toJSON(): Gen1.Side;
44
45
  static init(gen: Generation, lookup: Lookup, team: Partial<PokemonSet>[], data: DataView, offset: number): void;
45
- static encode(gen: Generation, lookup: Lookup, battle: Data<Gen1.Battle>, side: Gen1.Side, data: DataView, offset: number): void;
46
+ static encode(gen: Generation, showdown: boolean, lookup: Lookup, battle: Data<Gen1.Battle>, side: Gen1.Side, data: DataView, offset: number): void;
46
47
  }
47
48
  export declare class Pokemon implements Gen1.Pokemon {
48
49
  static Volatiles: {
@@ -77,11 +78,11 @@ export declare class Pokemon implements Gen1.Pokemon {
77
78
  toxic: number;
78
79
  };
79
80
  private get sleep();
80
- private get self();
81
+ private get ext();
81
82
  private get toxic();
82
83
  toJSON(): Gen1.Pokemon;
83
84
  static init(gen: Generation, lookup: Lookup, set: Partial<PokemonSet>, data: DataView, offset: number, index: number): void;
84
- static encodeStored(gen: Generation, lookup: Lookup, pokemon: Gen1.Pokemon, data: DataView, offset: number): void;
85
+ static encodeStored(gen: Generation, showdown: boolean, lookup: Lookup, pokemon: Gen1.Pokemon, data: DataView, offset: number): void;
85
86
  static encodeActive(gen: Generation, lookup: Lookup, battle: Data<Gen1.Battle>, pokemon: Gen1.Pokemon | undefined, data: DataView, offset: number): void;
86
87
  }
87
88
  export declare class StoredPokemon {
package/build/pkg/gen1.js CHANGED
@@ -50,7 +50,7 @@ class Battle {
50
50
  this.config = config;
51
51
  this.lookup = lookup;
52
52
  this.data = data;
53
- this.options = new ArrayBuffer(addon.size(0, 'options'));
53
+ this.options = new ArrayBuffer(addon.size(0, 'choices'));
54
54
  this.buf = config.log ? new ArrayBuffer(addon.size(0, 'log')) : undefined;
55
55
  this.log = config.log ? new DataView(this.buf) : undefined;
56
56
  this.cache = [undefined, undefined];
@@ -86,7 +86,7 @@ class Battle {
86
86
  return this.data.getUint16(OFFSETS.Battle.last_damage, data_2.LE);
87
87
  }
88
88
  get prng() {
89
- const offset = OFFSETS.Battle.last_selected_indexes + (this.config.showdown ? 4 : 1);
89
+ const offset = OFFSETS.Battle.last_moves + (this.config.showdown ? 4 : 2);
90
90
  // Pokémon Showdown's PRNGSeed is always big-endian
91
91
  const seed = [0, 0, 0, 0];
92
92
  if (this.config.showdown) {
@@ -99,10 +99,10 @@ class Battle {
99
99
  // Gen12RNG seed requires an index, but if we always rotate the seed based on the index when
100
100
  // reading we can avoid tracking the index as it will always be zero. NB: the exact bits of
101
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++) {
102
+ const index = this.data.getUint8(offset + 9);
103
+ for (let i = 0; i < 9; i++) {
104
104
  const j = i - index;
105
- seed[j < 0 ? j + 10 : j] = this.data.getUint8(offset + i);
105
+ seed[j < 0 ? j + 9 : j] = this.data.getUint8(offset + i);
106
106
  }
107
107
  }
108
108
  return seed;
@@ -127,20 +127,25 @@ class Battle {
127
127
  const buf = new ArrayBuffer(SIZES.Battle);
128
128
  const data = new DataView(buf);
129
129
  let offset = OFFSETS.Battle.p1;
130
- const indexes = [];
130
+ const lastMoves = [];
131
131
  for (const side of battle.sides) {
132
- Side.encode(gen, lookup, battle, side, data, offset);
132
+ Side.encode(gen, !!options.showdown, lookup, battle, side, data, offset);
133
133
  offset += SIZES.Side;
134
- indexes.push(side.lastSelectedIndex ?? 0);
134
+ if (options.showdown) {
135
+ lastMoves.push(+!!side.lastMoveCounterable);
136
+ lastMoves.push((side.lastMoveIndex ?? 0));
137
+ }
138
+ else {
139
+ lastMoves.push((+!!side.lastMoveCounterable << 4) | (side.lastMoveIndex ?? 0));
140
+ }
135
141
  }
136
142
  data.setUint16(OFFSETS.Battle.turn, battle.turn, data_2.LE);
137
143
  data.setUint16(OFFSETS.Battle.last_damage, battle.lastDamage, data_2.LE);
144
+ data.setUint8(OFFSETS.Battle.last_moves, lastMoves[0]);
145
+ data.setUint8(OFFSETS.Battle.last_moves + 1, lastMoves[1]);
138
146
  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]);
147
+ data.setUint8(OFFSETS.Battle.last_moves + 2, lastMoves[2]);
148
+ data.setUint8(OFFSETS.Battle.last_moves + 3, lastMoves[3]);
144
149
  }
145
150
  encodePRNG(data, battle.prng);
146
151
  return new Battle(lookup, data, options);
@@ -201,25 +206,38 @@ class Side {
201
206
  const m = this.data.getUint8(this.offset + OFFSETS.Side.last_selected_move);
202
207
  return m === 0 ? undefined : this.lookup.moveByNum(m);
203
208
  }
204
- get lastSelectedIndex() {
209
+ get lastMoveIndex() {
205
210
  let m;
206
211
  if (this.battle.config.showdown) {
207
212
  const off = this.id === 'p1' ? 0 : 2;
208
- m = this.data.getUint16(OFFSETS.Battle.last_selected_indexes + off, data_2.LE);
213
+ m = this.data.getUint8(OFFSETS.Battle.last_moves + off);
209
214
  }
210
215
  else {
211
- m = this.data.getUint8(OFFSETS.Battle.last_selected_indexes);
212
- m = this.id === 'p1' ? (m & 0xF) : (m >> 4);
216
+ const off = this.id === 'p1' ? 0 : 1;
217
+ m = this.data.getUint8(OFFSETS.Battle.last_moves + off) & 0x0F;
213
218
  }
214
219
  return m === 0 ? undefined : m;
215
220
  }
221
+ get lastMoveCounterable() {
222
+ let m;
223
+ if (this.battle.config.showdown) {
224
+ const off = this.id === 'p1' ? 1 : 3;
225
+ m = this.data.getUint8(OFFSETS.Battle.last_moves + off);
226
+ }
227
+ else {
228
+ const off = this.id === 'p1' ? 0 : 1;
229
+ m = this.data.getUint8(OFFSETS.Battle.last_moves + off) & 0xF0;
230
+ }
231
+ return m !== 0;
232
+ }
216
233
  toJSON() {
217
234
  return {
218
235
  active: this.active?.toJSON(),
219
236
  pokemon: Array.from(this.pokemon).map(p => p.toJSON()),
220
237
  lastSelectedMove: this.lastSelectedMove,
221
238
  lastUsedMove: this.lastUsedMove,
222
- lastSelectedIndex: this.lastSelectedIndex,
239
+ lastMoveIndex: this.lastMoveIndex,
240
+ lastMoveCounterable: this.lastMoveCounterable,
223
241
  };
224
242
  }
225
243
  static init(gen, lookup, team, data, offset) {
@@ -230,11 +248,11 @@ class Side {
230
248
  data.setUint8(offset + OFFSETS.Side.order + i, i + 1);
231
249
  }
232
250
  }
233
- static encode(gen, lookup, battle, side, data, offset) {
251
+ static encode(gen, showdown, lookup, battle, side, data, offset) {
234
252
  let i = 0;
235
253
  for (const pokemon of side.pokemon) {
236
254
  const off = offset + OFFSETS.Side.pokemon + SIZES.Pokemon * (pokemon.position - 1);
237
- Pokemon.encodeStored(gen, lookup, pokemon, data, off);
255
+ Pokemon.encodeStored(gen, showdown, lookup, pokemon, data, off);
238
256
  data.setUint8(offset + OFFSETS.Side.order + (pokemon.position - 1), i + 1);
239
257
  i++;
240
258
  }
@@ -424,13 +442,14 @@ class Pokemon {
424
442
  return status === 'psn' && this.toxic ? 'tox' : status;
425
443
  }
426
444
  get statusData() {
427
- return { sleep: this.sleep, self: this.self, toxic: this.toxic };
445
+ const sleep = this.sleep;
446
+ return { sleep, self: !!sleep && this.ext, toxic: this.toxic };
428
447
  }
429
448
  get sleep() {
430
449
  const val = this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.status);
431
450
  return val & 0b111;
432
451
  }
433
- get self() {
452
+ get ext() {
434
453
  return (this.data.getUint8(this.offset.stored + OFFSETS.Pokemon.status) >> 7) === 1;
435
454
  }
436
455
  get toxic() {
@@ -480,7 +499,7 @@ class Pokemon {
480
499
  data.setUint8(stored + OFFSETS.Pokemon.types, encodeTypes(lookup, species.types));
481
500
  data.setUint8(stored + OFFSETS.Pokemon.level, set.level ?? 100);
482
501
  }
483
- static encodeStored(gen, lookup, pokemon, data, offset) {
502
+ static encodeStored(gen, showdown, lookup, pokemon, data, offset) {
484
503
  let off = 0;
485
504
  const stats = pokemon.stored.stats;
486
505
  for (const stat of gen.stats) {
@@ -499,7 +518,7 @@ class Pokemon {
499
518
  data.setUint8(offset + OFFSETS.Pokemon.species, species.num);
500
519
  data.setUint8(offset + OFFSETS.Pokemon.types, encodeTypes(lookup, species.types));
501
520
  data.setUint8(offset + OFFSETS.Pokemon.level, pokemon.level);
502
- data.setUint8(offset + OFFSETS.Pokemon.status, encodeStatus(pokemon));
521
+ data.setUint8(offset + OFFSETS.Pokemon.status, encodeStatus(pokemon, showdown));
503
522
  }
504
523
  static encodeActive(gen, lookup, battle, pokemon, data, offset) {
505
524
  if (!pokemon)
@@ -620,7 +639,7 @@ class StoredPokemon {
620
639
  }
621
640
  exports.StoredPokemon = StoredPokemon;
622
641
  function encodePRNG(data, seed) {
623
- const offset = OFFSETS.Battle.last_damage + 2 + (seed.length === 4 ? 4 : 1);
642
+ const offset = OFFSETS.Battle.last_damage + 2 + (seed.length === 4 ? 4 : 2);
624
643
  if (seed.length === 4) {
625
644
  data.setUint16(offset, seed[data_2.LE ? 3 : 0], data_2.LE);
626
645
  data.setUint16(offset + 2, seed[data_2.LE ? 2 : 1], data_2.LE);
@@ -628,7 +647,7 @@ function encodePRNG(data, seed) {
628
647
  data.setUint16(offset + 6, seed[data_2.LE ? 0 : 3], data_2.LE);
629
648
  }
630
649
  else {
631
- for (let i = 0; i < 10; i++) {
650
+ for (let i = 0; i < 9; i++) {
632
651
  data.setUint8(offset + i, seed[i]);
633
652
  }
634
653
  // NB: ArrayBuffer is zero initialized already so we don't need to set the index to zero
@@ -646,18 +665,15 @@ function decodeSigned(n) {
646
665
  function encodeSigned(n) {
647
666
  return (n < 0) ? 0b10000 + n : n;
648
667
  }
649
- function encodeStatus(pokemon) {
668
+ function encodeStatus(pokemon, showdown) {
650
669
  if (pokemon.statusData.sleep) {
651
670
  if (pokemon.status !== 'slp') {
652
671
  throw new Error('Pokemon is not asleep but has non-zero sleep turns');
653
672
  }
654
673
  return (pokemon.statusData.self ? 0x80 : 0) | pokemon.statusData.sleep;
655
674
  }
656
- else if (pokemon.statusData.self) {
657
- throw new Error('Pokemon is not asleep but has self-inflicted sleep bit set');
658
- }
659
675
  switch (pokemon.status) {
660
- case 'tox':
676
+ case 'tox': return showdown ? 0b10001000 : 1 << 3;
661
677
  case 'psn': return 1 << 3;
662
678
  case 'brn': return 1 << 4;
663
679
  case 'frz': return 1 << 5;