@pkmn/engine 0.1.0-dev.6e63f5e4 → 0.1.0-dev.6ebbd3bc

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 (84) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +51 -23
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +44 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +97 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +25 -3
  13. package/build/pkg/addon.js +39 -71
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/ids.json +12 -12
  16. package/build/pkg/data/layout.json +17 -4
  17. package/build/pkg/data/protocol.json +72 -19
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +13 -17
  21. package/build/pkg/gen1.js +8 -12
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +40 -91
  24. package/build/pkg/index.js +6 -63
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +20 -1
  27. package/build/pkg/protocol.js +123 -102
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +36 -69
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +141 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +142 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +349 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +18 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +312 -102
  40. package/package.json +62 -123
  41. package/src/bin/install-pkmn-engine +30 -20
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +254 -0
  44. package/src/lib/{binding → bindings}/node.zig +8 -21
  45. package/src/lib/bindings/wasm.zig +59 -0
  46. package/src/lib/common/array.zig +77 -0
  47. package/src/lib/common/data.zig +8 -8
  48. package/src/lib/common/debug.zig +39 -8
  49. package/src/lib/common/js.zig +4 -2
  50. package/src/lib/common/optional.zig +21 -10
  51. package/src/lib/common/options.zig +6 -0
  52. package/src/lib/common/protocol.zig +687 -365
  53. package/src/lib/common/rational.zig +131 -35
  54. package/src/lib/common/rng.zig +16 -9
  55. package/src/lib/common/util.zig +50 -1
  56. package/src/lib/gen1/calc.zig +687 -160
  57. package/src/lib/gen1/chance.zig +434 -307
  58. package/src/lib/gen1/data/moves.zig +26 -14
  59. package/src/lib/gen1/data/species.zig +4 -6
  60. package/src/lib/gen1/data/types.zig +7 -8
  61. package/src/lib/gen1/data.zig +44 -47
  62. package/src/lib/gen1/helpers.zig +91 -562
  63. package/src/lib/gen1/mechanics.zig +641 -557
  64. package/src/lib/gen2/calc.zig +153 -0
  65. package/src/lib/gen2/chance.zig +902 -0
  66. package/src/lib/gen2/data/items.zig +13 -21
  67. package/src/lib/gen2/data/moves.zig +67 -47
  68. package/src/lib/gen2/data/species.zig +2 -4
  69. package/src/lib/gen2/data/types.zig +134 -32
  70. package/src/lib/gen2/data.zig +57 -54
  71. package/src/lib/gen2/generated.zig +801 -0
  72. package/src/lib/gen2/helpers.zig +46 -25
  73. package/src/lib/gen2/mechanics.zig +2778 -375
  74. package/src/lib/node.zig +18 -0
  75. package/src/lib/pkmn.zig +37 -15
  76. package/src/lib/wasm.zig +17 -0
  77. package/src/tools/display/dom.ts +96 -0
  78. package/src/tools/display/ui.css +354 -0
  79. package/build/test/display.js +0 -400
  80. package/build/test/display.js.map +0 -1
  81. package/src/lib/binding/wasm.zig +0 -12
  82. package/src/lib/gen1/README.md +0 -894
  83. package/src/lib/gen2/README.md +0 -383
  84. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,16 +1,21 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
4
- const data = @import("./data.zig");
5
- const options = @import("./options.zig");
2
+ const data = @import("data.zig");
3
+ const options = @import("options.zig");
4
+ const pkmn = @import("../pkmn.zig");
5
+ const std = @import("std");
6
6
 
7
7
  const assert = std.debug.assert;
8
+ const enabled = options.log;
9
+ const ID = data.ID;
10
+ const Player = data.Player;
8
11
  const print = std.debug.print;
9
12
 
10
- const enabled = options.log;
13
+ const endian = builtin.cpu.arch.endian();
14
+ const Endian = std.builtin.Endian;
15
+ const big = if (@hasField(Endian, "big")) Endian.big else Endian.Big;
16
+ const little = if (@hasField(Endian, "little")) Endian.little else Endian.Little;
11
17
 
12
- const Player = data.Player;
13
- const ID = data.ID;
18
+ const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
14
19
 
15
20
  pub const ArgType = enum(u8) {
16
21
  None,
@@ -69,7 +74,7 @@ pub const ArgType = enum(u8) {
69
74
  EndAbility,
70
75
  ClearNegativeBoost,
71
76
  FormeChange,
72
- NoTarget,
77
+ LastNoTarget, // FIXME
73
78
 
74
79
  // Gen 4
75
80
  SwapBoost,
@@ -102,6 +107,7 @@ pub const ArgType = enum(u8) {
102
107
  // Gen 8
103
108
  CanDynamax,
104
109
  SwapSideConditions,
110
+ UpdatePoke,
105
111
 
106
112
  // Gen 9
107
113
  Terastallize,
@@ -121,6 +127,8 @@ pub const Cant = enum(u8) {
121
127
  Disable,
122
128
  Recharge,
123
129
  PP,
130
+
131
+ Attract,
124
132
  };
125
133
 
126
134
  pub const Heal = enum(u8) {
@@ -129,6 +137,9 @@ pub const Heal = enum(u8) {
129
137
  Drain,
130
138
 
131
139
  Leftovers,
140
+ Berry,
141
+ BerryJuice,
142
+ GoldBerry,
132
143
  };
133
144
 
134
145
  pub const Damage = enum(u8) {
@@ -139,7 +150,16 @@ pub const Damage = enum(u8) {
139
150
  LeechSeed,
140
151
  RecoilOf,
141
152
 
153
+ Bind,
154
+ Wrap,
155
+ FireSpin,
156
+ Clamp,
157
+ Whirlpool,
142
158
  Spikes,
159
+
160
+ Nightmare,
161
+ Curse,
162
+ Sandstorm,
143
163
  };
144
164
 
145
165
  pub const Status = enum(u8) {
@@ -182,10 +202,26 @@ pub const Activate = enum(u8) {
182
202
  Haze,
183
203
  Mist,
184
204
  Struggle,
185
- Substitute,
205
+ Substitute, // FIXME SubstituteDamage
186
206
  Splash,
187
207
 
208
+ Rage, // TODO
209
+
188
210
  Attract,
211
+ LockOn,
212
+ Bind,
213
+ Wrap,
214
+ FireSpin,
215
+ Clamp,
216
+ Whirlpool,
217
+ SubstituteBlock,
218
+
219
+ DestinyBond,
220
+
221
+ BeatUp, // FIXME of
222
+
223
+ Magnitude,
224
+ Spite,
189
225
  };
190
226
 
191
227
  pub const Start = enum(u8) {
@@ -203,6 +239,19 @@ pub const Start = enum(u8) {
203
239
 
204
240
  Disable,
205
241
  Mimic,
242
+
243
+ Attract,
244
+ Nightmare,
245
+ Curse,
246
+ Foresight,
247
+ Encore,
248
+ FutureSight,
249
+
250
+ PerishSong0,
251
+ PerishSong1,
252
+ PerishSong2,
253
+ PerishSong3,
254
+ PerishSong3Silent,
206
255
  };
207
256
 
208
257
  pub const End = enum(u8) {
@@ -211,19 +260,30 @@ pub const End = enum(u8) {
211
260
  Bide,
212
261
  Substitute,
213
262
 
214
- FutureSight,
263
+ Nightmare,
264
+ Curse,
265
+ Foresight,
215
266
  Encore,
267
+ FutureSight,
268
+ LeechSeed,
269
+
270
+ Bind,
271
+ Wrap,
272
+ FireSpin,
273
+ Clamp,
274
+ Whirlpool,
216
275
 
217
- // Silent
218
276
  DisableSilent,
219
277
  ConfusionSilent,
220
-
221
- Mist,
222
- FocusEnergy,
223
- LeechSeed,
224
- Toxic,
225
- LightScreen,
226
- Reflect,
278
+ MistSilent,
279
+ FocusEnergySilent,
280
+ LeechSeedSilent,
281
+ ToxicSilent,
282
+ LightScreenSilent,
283
+ ReflectSilent,
284
+ BideSilent,
285
+
286
+ LeechSeedFrom,
227
287
  };
228
288
 
229
289
  pub const Immune = enum(u8) {
@@ -231,21 +291,28 @@ pub const Immune = enum(u8) {
231
291
  OHKO,
232
292
  };
233
293
 
234
- pub const SideStart = enum(u8) {
235
- Safeguard,
236
- Reflect,
237
- LightScreen,
238
- Spikes,
294
+ pub const EndItem = enum(u8) {
295
+ None,
296
+ Eat,
239
297
  };
240
298
 
241
- pub const SideEnd = enum(u8) {
299
+ pub const SetHP = enum(u8) {
300
+ None,
301
+ Silent,
302
+ };
303
+
304
+ pub const Side = enum(u8) {
242
305
  Safeguard,
243
306
  Reflect,
244
307
  LightScreen,
245
-
246
308
  Spikes,
247
309
  };
248
310
 
311
+ pub const Weather = enum(u8) {
312
+ None,
313
+ Upkeep,
314
+ };
315
+
249
316
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
250
317
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
251
318
  /// entirely with `options.log`.
@@ -260,410 +327,488 @@ pub fn Log(comptime Writer: type) type {
260
327
 
261
328
  pub const Error = Writer.Error;
262
329
 
263
- pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
330
+ // source: ID, move: <Move>, target: ID, from?: <Move>
331
+ pub fn move(self: Self, args: anytype) Error!void {
264
332
  if (!enabled) return;
265
333
 
266
- assert(m != .None);
334
+ assert(args[1] != .None);
267
335
  try self.writer.writeAll(&.{
268
336
  @intFromEnum(ArgType.Move),
269
- @as(u8, @bitCast(source)),
270
- @intFromEnum(m),
271
- @as(u8, @bitCast(target)),
272
- });
273
- const none = &[_]u8{@intFromEnum(Move.None)};
274
- try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
275
- .Null => none,
276
- .Optional => if (from) |f| blk: {
277
- assert(@intFromEnum(f) != 0);
278
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
279
- } else none,
280
- else => blk: {
281
- assert(@intFromEnum(from) != 0);
282
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(from) };
283
- },
337
+ @as(u8, @bitCast(args[0])),
338
+ @intFromEnum(args[1]),
339
+ @as(u8, @bitCast(args[2])),
284
340
  });
341
+ try self.writer.writeAll(if (args.len < 4 or @intFromEnum(args[3]) == 0)
342
+ &.{@intFromEnum(Move.None)}
343
+ else
344
+ &.{ @intFromEnum(Move.From), @intFromEnum(args[3]) });
345
+ }
346
+
347
+ // ident: ID, pokemon: <Pokemon>
348
+ pub fn switched(self: Self, args: anytype) Error!void {
349
+ return switchDrag(self, .Switch, args);
285
350
  }
286
351
 
287
- pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
352
+ // ident: ID, pokemon: <Pokemon>
353
+ fn switchDrag(self: Self, arg: ArgType, args: anytype) Error!void {
288
354
  if (!enabled) return;
289
355
 
290
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
291
- if (@hasField(@TypeOf(pokemon.*), "dvs")) {
356
+ try self.writer.writeAll(&.{ @intFromEnum(arg), @as(u8, @bitCast(args[0])) });
357
+ if (@hasField(@TypeOf(args[1].*), "dvs")) {
292
358
  try self.writer.writeAll(&.{
293
- @intFromEnum(pokemon.species),
294
- @intFromEnum(pokemon.dvs.gender),
295
- pokemon.level,
359
+ @intFromEnum(args[1].species),
360
+ @intFromEnum(args[1].dvs.gender),
361
+ args[1].level,
296
362
  });
297
363
  } else {
298
- try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
364
+ try self.writer.writeAll(&.{ @intFromEnum(args[1].species), args[1].level });
299
365
  }
300
- try self.writer.writeIntNative(u16, pokemon.hp);
301
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
302
- try self.writer.writeAll(&.{pokemon.status});
366
+ try self.writer.writeInt(u16, args[1].hp, endian);
367
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
368
+ try self.writer.writeAll(&.{args[1].status});
303
369
  }
304
370
 
305
- pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
371
+ // ident: ID, reason: Cant
372
+ // ident: ID, reason: .Disable, move: <Move>
373
+ pub fn cant(self: Self, args: anytype) Error!void {
306
374
  if (!enabled) return;
307
375
 
308
- assert(reason != .Disable);
376
+ assert(args[1] != .Disable or args.len == 3);
309
377
  try self.writer.writeAll(&.{
310
378
  @intFromEnum(ArgType.Cant),
311
- @as(u8, @bitCast(ident)),
312
- @intFromEnum(reason),
379
+ @as(u8, @bitCast(args[0])),
380
+ @intFromEnum(@as(Cant, args[1])),
313
381
  });
382
+ if (args.len == 3) try self.writer.writeByte(@intFromEnum(args[2]));
314
383
  }
315
384
 
316
- pub fn disabled(self: Self, ident: ID, m: anytype) Error!void {
385
+ // ident: ID, done: bool
386
+ pub fn faint(self: Self, args: anytype) Error!void {
317
387
  if (!enabled) return;
318
388
 
319
- try self.writer.writeAll(&.{
320
- @intFromEnum(ArgType.Cant),
321
- @as(u8, @bitCast(ident)),
322
- @intFromEnum(Cant.Disable),
323
- @intFromEnum(m),
324
- });
389
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(args[0])) });
390
+ if (args[1]) try self.writer.writeByte(@intFromEnum(ArgType.None));
325
391
  }
326
392
 
327
- pub fn faint(self: Self, ident: ID, done: bool) Error!void {
328
- if (!enabled) return;
329
-
330
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(ident)) });
331
- if (done) try self.writer.writeByte(@intFromEnum(ArgType.None));
332
- }
333
-
334
- pub fn turn(self: Self, num: u16) Error!void {
393
+ // num: u16
394
+ pub fn turn(self: Self, args: anytype) Error!void {
335
395
  if (!enabled) return;
336
396
 
337
397
  try self.writer.writeByte(@intFromEnum(ArgType.Turn));
338
- try self.writer.writeIntNative(u16, num);
398
+ try self.writer.writeInt(u16, args[0], endian);
339
399
  try self.writer.writeByte(@intFromEnum(ArgType.None));
340
400
  }
341
401
 
342
- pub fn win(self: Self, player: Player) Error!void {
402
+ // player: Player
403
+ pub fn win(self: Self, args: anytype) Error!void {
343
404
  if (!enabled) return;
344
405
 
345
406
  try self.writer.writeAll(&.{
346
407
  @intFromEnum(ArgType.Win),
347
- @intFromEnum(player),
408
+ @intFromEnum(@as(Player, args[0])),
348
409
  @intFromEnum(ArgType.None),
349
410
  });
350
411
  }
351
412
 
352
- pub fn tie(self: Self) Error!void {
413
+ // -
414
+ pub fn tie(self: Self, _: anytype) Error!void {
353
415
  if (!enabled) return;
354
416
 
355
417
  try self.writer.writeAll(&.{ @intFromEnum(ArgType.Tie), @intFromEnum(ArgType.None) });
356
418
  }
357
419
 
358
- pub fn damage(self: Self, ident: ID, pokemon: anytype, reason: Damage) Error!void {
359
- if (!enabled) return;
360
-
361
- assert(@intFromEnum(reason) <= @intFromEnum(Damage.LeechSeed));
362
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
363
- try self.writer.writeIntNative(u16, pokemon.hp);
364
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
365
- try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
366
- }
367
-
368
- pub fn damageOf(
369
- self: Self,
370
- ident: ID,
371
- pokemon: anytype,
372
- reason: Damage,
373
- source: ID,
374
- ) Error!void {
420
+ // ident: ID, pokemon: <Pokemon>, reason: Damage
421
+ // ident: ID, pokemon: <Pokemon>, reason: .RecoilOf, source: ID
422
+ pub fn damage(self: Self, args: anytype) Error!void {
375
423
  if (!enabled) return;
376
424
 
377
- assert(@intFromEnum(reason) == @intFromEnum(Damage.RecoilOf));
378
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
379
- try self.writer.writeIntNative(u16, pokemon.hp);
380
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
425
+ assert(args[2] != .RecoilOf or args.len == 4);
381
426
  try self.writer.writeAll(&.{
382
- pokemon.status,
383
- @intFromEnum(reason),
384
- @as(u8, @bitCast(source)),
385
- });
386
- }
387
-
388
- pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) Error!void {
389
- if (!enabled) return;
390
-
391
- assert(reason != .Drain);
392
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(ident)) });
393
- try self.writer.writeIntNative(u16, pokemon.hp);
394
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
395
- try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
396
- }
397
-
398
- pub fn drain(self: Self, source: ID, pokemon: anytype, target: ID) Error!void {
399
- if (!enabled) return;
400
-
401
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(source)) });
402
- try self.writer.writeIntNative(u16, pokemon.hp);
403
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
404
- try self.writer.writeAll(&.{
405
- pokemon.status,
406
- @intFromEnum(Heal.Drain),
407
- @as(u8, @bitCast(target)),
427
+ @intFromEnum(ArgType.Damage),
428
+ @as(u8, @bitCast(args[0])),
408
429
  });
430
+ try self.writer.writeInt(u16, args[1].hp, endian);
431
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
432
+ try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Damage, args[2])) });
433
+ if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
409
434
  }
410
435
 
411
- pub fn status(self: Self, ident: ID, value: u8, reason: Status) Error!void {
436
+ // ident: ID, pokemon: <Pokemon>, reason: Heal
437
+ // ident: ID, pokemon: <Pokemon>, reason: .Drain, target: ID
438
+ pub fn heal(self: Self, args: anytype) Error!void {
412
439
  if (!enabled) return;
413
440
 
414
- assert(reason != .From);
415
- try self.writer.writeAll(&.{
416
- @intFromEnum(ArgType.Status),
417
- @as(u8, @bitCast(ident)),
418
- value,
419
- @intFromEnum(reason),
420
- });
441
+ assert(args[2] != .Drain or args.len == 4);
442
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(args[0])) });
443
+ try self.writer.writeInt(u16, args[1].hp, endian);
444
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
445
+ try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Heal, args[2])) });
446
+ if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
421
447
  }
422
448
 
423
- pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
449
+ // ident: ID, value: u8, reason: Status
450
+ // ident: ID, value: u8, reason: .From, m: <Move>
451
+ pub fn status(self: Self, args: anytype) Error!void {
424
452
  if (!enabled) return;
425
453
 
454
+ assert(args[2] != .From or args.len == 4);
426
455
  try self.writer.writeAll(&.{
427
456
  @intFromEnum(ArgType.Status),
428
- @as(u8, @bitCast(ident)),
429
- value,
430
- @intFromEnum(Status.From),
431
- @intFromEnum(m),
457
+ @as(u8, @bitCast(args[0])),
458
+ args[1],
459
+ @intFromEnum(@as(Status, args[2])),
432
460
  });
461
+ if (args.len == 4) try self.writer.writeByte(@intFromEnum(args[3]));
433
462
  }
434
463
 
435
- pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
464
+ // ident: ID, value: u8, reason: CureStatus
465
+ pub fn curestatus(self: Self, args: anytype) Error!void {
436
466
  if (!enabled) return;
437
467
 
438
468
  try self.writer.writeAll(&.{
439
469
  @intFromEnum(ArgType.CureStatus),
440
- @as(u8, @bitCast(ident)),
441
- value,
442
- @intFromEnum(reason),
470
+ @as(u8, @bitCast(args[0])),
471
+ args[1],
472
+ @intFromEnum(@as(CureStatus, args[2])),
443
473
  });
444
474
  }
445
475
 
446
- pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
476
+ // ident: ID, reason: Boost, num: i8
477
+ pub fn boost(self: Self, args: anytype) Error!void {
447
478
  if (!enabled) return;
448
479
 
449
- assert(num != 0 and (reason != .Rage or num > 0));
480
+ assert(args[2] != 0 and (args[1] != .Rage or args[2] > 0));
450
481
  try self.writer.writeAll(&.{
451
482
  @intFromEnum(ArgType.Boost),
452
- @as(u8, @bitCast(ident)),
453
- @intFromEnum(reason),
454
- @as(u8, @intCast(num + 6)),
483
+ @as(u8, @bitCast(args[0])),
484
+ @intFromEnum(@as(Boost, args[1])),
485
+ @as(u8, @intCast(@as(i8, args[2]) + 6)),
455
486
  });
456
487
  }
457
488
 
458
- pub fn clearallboost(self: Self) Error!void {
489
+ // -
490
+ pub fn clearallboost(self: Self, _: anytype) Error!void {
459
491
  if (!enabled) return;
460
492
 
461
493
  try self.writer.writeAll(&.{@intFromEnum(ArgType.ClearAllBoost)});
462
494
  }
463
495
 
464
- pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
496
+ // ident: ID, reason: Fail
497
+ pub fn fail(self: Self, args: anytype) Error!void {
465
498
  if (!enabled) return;
466
499
 
467
500
  try self.writer.writeAll(&.{
468
501
  @intFromEnum(ArgType.Fail),
469
- @as(u8, @bitCast(ident)),
470
- @intFromEnum(reason),
502
+ @as(u8, @bitCast(args[0])),
503
+ @intFromEnum(@as(Fail, args[1])),
471
504
  });
472
505
  }
473
506
 
474
- pub fn miss(self: Self, source: ID) Error!void {
507
+ // source: ID
508
+ pub fn miss(self: Self, args: anytype) Error!void {
475
509
  if (!enabled) return;
476
510
 
477
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(source)) });
511
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(args[0])) });
478
512
  }
479
513
 
480
- pub fn hitcount(self: Self, ident: ID, num: u8) Error!void {
514
+ // ident: ID, num: u8
515
+ pub fn hitcount(self: Self, args: anytype) Error!void {
481
516
  if (!enabled) return;
482
517
 
483
518
  try self.writer.writeAll(&.{
484
519
  @intFromEnum(ArgType.HitCount),
485
- @as(u8, @bitCast(ident)),
486
- num,
520
+ @as(u8, @bitCast(args[0])),
521
+ args[1],
487
522
  });
488
523
  }
489
524
 
490
- pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
525
+ // source: ID, m: <Move>
526
+ pub fn prepare(self: Self, args: anytype) Error!void {
491
527
  if (!enabled) return;
492
528
 
493
529
  try self.writer.writeAll(&.{
494
530
  @intFromEnum(ArgType.Prepare),
495
- @as(u8, @bitCast(source)),
496
- @intFromEnum(m),
531
+ @as(u8, @bitCast(args[0])),
532
+ @intFromEnum(args[1]),
497
533
  });
498
534
  }
499
535
 
500
- pub fn mustrecharge(self: Self, ident: ID) Error!void {
536
+ // ident: ID
537
+ pub fn mustrecharge(self: Self, args: anytype) Error!void {
501
538
  if (!enabled) return;
502
539
 
503
540
  try self.writer.writeAll(&.{
504
541
  @intFromEnum(ArgType.MustRecharge),
505
- @as(u8, @bitCast(ident)),
542
+ @as(u8, @bitCast(args[0])),
506
543
  });
507
544
  }
508
545
 
509
- pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
546
+ // ident: ID, reason: Activate
547
+ // ident: ID, reason: .SubstituteBlock, m: <Move>
548
+ // ident: ID, reason: .Magnitude, num: u8
549
+ // ident: ID, reason: .Spite, m: <Move>, num: u8
550
+ pub fn activate(self: Self, args: anytype) Error!void {
510
551
  if (!enabled) return;
511
552
 
553
+ assert(args.len <= 4);
554
+ const reason: Activate = args[1];
512
555
  try self.writer.writeAll(&.{
513
556
  @intFromEnum(ArgType.Activate),
514
- @as(u8, @bitCast(ident)),
557
+ @as(u8, @bitCast(args[0])),
515
558
  @intFromEnum(reason),
516
559
  });
560
+ if (args.len == 3) {
561
+ if (reason == .SubstituteBlock) {
562
+ try self.writer.writeByte(@intFromEnum(args[2]));
563
+ } else {
564
+ assert(reason == .Magnitude);
565
+ try self.writer.writeByte(args[2]);
566
+ }
567
+ } else if (args.len == 4) {
568
+ assert(reason == .Spite);
569
+ try self.writer.writeAll(&.{ @intFromEnum(args[2]), args[3] });
570
+ }
517
571
  }
518
572
 
519
- pub fn fieldactivate(self: Self) Error!void {
573
+ // -
574
+ pub fn fieldactivate(self: Self, _: anytype) Error!void {
520
575
  if (!enabled) return;
521
576
 
522
577
  try self.writer.writeAll(&.{@intFromEnum(ArgType.FieldActivate)});
523
578
  }
524
579
 
525
- pub fn start(self: Self, ident: ID, reason: Start) Error!void {
580
+ // ident: ID, reason: Start
581
+ // ident: ID, reason: .Disable | .Mimic | .Encore, m: <Move>
582
+ // ident: ID, reason: .TypeChange, types: <Types>, target: ID
583
+ pub fn start(self: Self, args: anytype) Error!void {
526
584
  if (!enabled) return;
527
585
 
528
- assert(@intFromEnum(reason) < @intFromEnum(Start.TypeChange));
586
+ assert(args.len <= 4);
587
+ const reason: Start = args[1];
529
588
  try self.writer.writeAll(&.{
530
589
  @intFromEnum(ArgType.Start),
531
- @as(u8, @bitCast(ident)),
590
+ @as(u8, @bitCast(args[0])),
532
591
  @intFromEnum(reason),
533
592
  });
593
+ if (args.len == 3) {
594
+ assert(reason == .Disable or reason == .Mimic or reason == .Encore);
595
+ try self.writer.writeByte(@intFromEnum(args[2]));
596
+ } else if (args.len == 4) {
597
+ assert(reason == .TypeChange);
598
+ // FIXME: u16 types in Generation II
599
+ try self.writer.writeAll(&.{
600
+ @as(u8, @bitCast(args[2])),
601
+ @as(u8, @bitCast(args[3])),
602
+ });
603
+ }
534
604
  }
535
605
 
536
- pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
606
+ // ident: ID, reason: End
607
+ pub fn end(self: Self, args: anytype) Error!void {
537
608
  if (!enabled) return;
538
609
 
539
610
  try self.writer.writeAll(&.{
540
- @intFromEnum(ArgType.Start),
541
- @as(u8, @bitCast(source)),
542
- @intFromEnum(Start.TypeChange),
543
- @as(u8, @bitCast(types)),
544
- @as(u8, @bitCast(target)),
611
+ @intFromEnum(ArgType.End),
612
+ @as(u8, @bitCast(args[0])),
613
+ @intFromEnum(@as(End, args[1])),
545
614
  });
546
615
  }
547
616
 
548
- pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) Error!void {
617
+ // -
618
+ pub fn ohko(self: Self, _: anytype) Error!void {
619
+ if (!enabled) return;
620
+
621
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
622
+ }
623
+
624
+ // ident: ID
625
+ pub fn crit(self: Self, args: anytype) Error!void {
626
+ if (!enabled) return;
627
+
628
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(args[0])) });
629
+ }
630
+
631
+ // ident: ID
632
+ pub fn supereffective(self: Self, args: anytype) Error!void {
549
633
  if (!enabled) return;
550
634
 
551
- assert(@intFromEnum(reason) > @intFromEnum(Start.TypeChange));
552
635
  try self.writer.writeAll(&.{
553
- @intFromEnum(ArgType.Start),
554
- @as(u8, @bitCast(ident)),
555
- @intFromEnum(reason),
556
- @intFromEnum(m),
636
+ @intFromEnum(ArgType.SuperEffective),
637
+ @as(u8, @bitCast(args[0])),
557
638
  });
558
639
  }
559
640
 
560
- pub fn end(self: Self, ident: ID, reason: End) Error!void {
641
+ // ident: ID
642
+ pub fn resisted(self: Self, args: anytype) Error!void {
561
643
  if (!enabled) return;
562
644
 
563
645
  try self.writer.writeAll(&.{
564
- @intFromEnum(ArgType.End),
565
- @as(u8, @bitCast(ident)),
566
- @intFromEnum(reason),
646
+ @intFromEnum(ArgType.Resisted),
647
+ @as(u8, @bitCast(args[0])),
567
648
  });
568
649
  }
569
650
 
570
- pub fn ohko(self: Self) Error!void {
651
+ // ident: ID, reason: Immune
652
+ pub fn immune(self: Self, args: anytype) Error!void {
571
653
  if (!enabled) return;
572
654
 
573
- try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
655
+ try self.writer.writeAll(&.{
656
+ @intFromEnum(ArgType.Immune),
657
+ @as(u8, @bitCast(args[0])),
658
+ @intFromEnum(@as(Immune, args[1])),
659
+ });
574
660
  }
575
661
 
576
- pub fn crit(self: Self, ident: ID) Error!void {
662
+ // source: ID, target: ID
663
+ pub fn transform(self: Self, args: anytype) Error!void {
577
664
  if (!enabled) return;
578
665
 
579
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(ident)) });
666
+ try self.writer.writeAll(&.{
667
+ @intFromEnum(ArgType.Transform),
668
+ @as(u8, @bitCast(args[0])),
669
+ @as(u8, @bitCast(args[1])),
670
+ });
671
+ }
672
+
673
+ // ident: ID, pokemon: <Pokemon>
674
+ pub fn drag(self: Self, args: anytype) Error!void {
675
+ return switchDrag(self, .Drag, args);
580
676
  }
581
677
 
582
- pub fn supereffective(self: Self, ident: ID) Error!void {
678
+ // target: ID, item: <Item>, source: ID
679
+ pub fn item(self: Self, args: anytype) Error!void {
583
680
  if (!enabled) return;
584
681
 
585
682
  try self.writer.writeAll(&.{
586
- @intFromEnum(ArgType.SuperEffective),
587
- @as(u8, @bitCast(ident)),
683
+ @intFromEnum(ArgType.Item),
684
+ @as(u8, @bitCast(args[0])),
685
+ @intFromEnum(args[1]),
686
+ @as(u8, @bitCast(args[2])),
588
687
  });
589
688
  }
590
689
 
591
- pub fn resisted(self: Self, ident: ID) Error!void {
690
+ // ident: ID, item: <Item>, reason: EndItem
691
+ pub fn enditem(self: Self, args: anytype) Error!void {
592
692
  if (!enabled) return;
593
693
 
594
694
  try self.writer.writeAll(&.{
595
- @intFromEnum(ArgType.Resisted),
596
- @as(u8, @bitCast(ident)),
695
+ @intFromEnum(ArgType.EndItem),
696
+ @as(u8, @bitCast(args[0])),
697
+ @intFromEnum(args[1]),
698
+ @intFromEnum(@as(EndItem, args[2])),
597
699
  });
598
700
  }
599
701
 
600
- pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
702
+ // source: ID
703
+ pub fn cureteam(self: Self, args: anytype) Error!void {
601
704
  if (!enabled) return;
602
705
 
603
706
  try self.writer.writeAll(&.{
604
- @intFromEnum(ArgType.Immune),
605
- @as(u8, @bitCast(ident)),
606
- @intFromEnum(reason),
707
+ @intFromEnum(ArgType.CureTeam),
708
+ @as(u8, @bitCast(args[0])),
607
709
  });
608
710
  }
609
711
 
610
- pub fn transform(self: Self, source: ID, target: ID) Error!void {
712
+ // ident: ID, pokemon: <Pokemon>, reason: SetHP
713
+ pub fn sethp(self: Self, args: anytype) Error!void {
714
+ if (!enabled) return;
715
+
716
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(args[0])) });
717
+ try self.writer.writeInt(u16, args[1].hp, endian);
718
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
719
+ try self.writer.writeAll(&.{args[1].status});
720
+ try self.writer.writeByte(@intFromEnum(@as(SetHP, args[2])));
721
+ }
722
+
723
+ // ident: ID, num: i8
724
+ pub fn setboost(self: Self, args: anytype) Error!void {
611
725
  if (!enabled) return;
612
726
 
613
727
  try self.writer.writeAll(&.{
614
- @intFromEnum(ArgType.Transform),
615
- @as(u8, @bitCast(source)),
616
- @as(u8, @bitCast(target)),
728
+ @intFromEnum(ArgType.SetBoost),
729
+ @as(u8, @bitCast(args[0])),
730
+ @as(u8, @intCast(args[1] + 6)),
617
731
  });
618
732
  }
619
733
 
620
- pub fn sidestart(self: Self, ident: ID, reason: SideStart) Error!void {
734
+ // source: ID, target: ID
735
+ pub fn copyboost(self: Self, args: anytype) Error!void {
736
+ if (!enabled) return;
737
+
738
+ try self.writer.writeAll(&.{
739
+ @intFromEnum(ArgType.CopyBoost),
740
+ @as(u8, @bitCast(args[0])),
741
+ @as(u8, @bitCast(args[1])),
742
+ });
743
+ }
744
+
745
+ // player: Player, reason: Side
746
+ pub fn sidestart(self: Self, args: anytype) Error!void {
621
747
  if (!enabled) return;
622
748
 
623
749
  try self.writer.writeAll(&.{
624
750
  @intFromEnum(ArgType.SideStart),
625
- @as(u8, @bitCast(ident)),
626
- @intFromEnum(reason),
751
+ @intFromEnum(@as(Player, args[0])),
752
+ @intFromEnum(@as(Side, args[1])),
627
753
  });
628
754
  }
629
755
 
630
- pub fn sideend(self: Self, ident: ID, reason: SideEnd) Error!void {
756
+ // player: Player, reason: Side
757
+ // player: Player, reason: .Spikes, source: ID
758
+ pub fn sideend(self: Self, args: anytype) Error!void {
631
759
  if (!enabled) return;
632
760
 
633
- assert(@intFromEnum(reason) != @intFromEnum(SideEnd.Spikes));
761
+ assert(args[1] != .Spikes or args.len == 3);
634
762
  try self.writer.writeAll(&.{
635
763
  @intFromEnum(ArgType.SideEnd),
636
- @as(u8, @bitCast(ident)),
637
- @intFromEnum(reason),
764
+ @intFromEnum(@as(Player, args[0])),
765
+ @intFromEnum(@as(Side, args[1])),
638
766
  });
767
+ if (args.len == 3) try self.writer.writeByte(@as(u8, @bitCast(args[2])));
768
+ }
769
+
770
+ // ident: ID, move: <Move>
771
+ pub fn singlemove(self: Self, args: anytype) Error!void {
772
+ return single(self, .SingleMove, args);
639
773
  }
640
774
 
641
- pub fn sideendFromOf(
642
- self: Self,
643
- ident: ID,
644
- reason: SideEnd,
645
- m: anytype,
646
- source: ID,
647
- ) Error!void {
775
+ // ident: ID, move: <Move>
776
+ pub fn singleturn(self: Self, args: anytype) Error!void {
777
+ return single(self, .SingleTurn, args);
778
+ }
779
+
780
+ // ident: ID, move: <Move>
781
+ fn single(self: Self, arg: ArgType, args: anytype) Error!void {
648
782
  if (!enabled) return;
649
783
 
650
- assert(@intFromEnum(reason) == @intFromEnum(SideEnd.Spikes));
651
784
  try self.writer.writeAll(&.{
652
- @intFromEnum(ArgType.SideEnd),
653
- @as(u8, @bitCast(ident)),
654
- @intFromEnum(reason),
655
- @intFromEnum(m),
656
- @as(u8, @bitCast(source)),
785
+ @intFromEnum(arg),
786
+ @as(u8, @bitCast(args[0])),
787
+ @intFromEnum(args[1]),
657
788
  });
658
789
  }
659
790
 
660
- pub fn laststill(self: Self) Error!void {
791
+ // weather: <Weather>, reason: Weather
792
+ pub fn weather(self: Self, args: anytype) Error!void {
793
+ if (!enabled) return;
794
+
795
+ assert(!(args[0] == .None and args[1] == .Upkeep));
796
+ try self.writer.writeAll(&.{
797
+ @intFromEnum(ArgType.Weather),
798
+ @intFromEnum(args[0]),
799
+ @intFromEnum(@as(Weather, args[1])),
800
+ });
801
+ }
802
+
803
+ // -
804
+ pub fn laststill(self: Self, _: anytype) Error!void {
661
805
  if (!enabled) return;
662
806
 
663
807
  try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
664
808
  }
665
809
 
666
- pub fn lastmiss(self: Self) Error!void {
810
+ // -
811
+ pub fn lastmiss(self: Self, _: anytype) Error!void {
667
812
  if (!enabled) return;
668
813
 
669
814
  try self.writer.writeAll(&.{@intFromEnum(ArgType.LastMiss)});
@@ -694,10 +839,10 @@ pub const ByteStream = struct {
694
839
  try self.stream.writeByte(byte);
695
840
  }
696
841
 
697
- pub fn writeIntNative(self: Writer, comptime T: type, value: T) Error!void {
842
+ pub fn writeInt(self: Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
698
843
  // TODO: rework this to write directly to the buffer?
699
- var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
700
- std.mem.writeIntNative(T, &bytes, value);
844
+ var bytes: [@divExact(@field(@typeInfo(T), @tagName(Int)).bits, 8)]u8 = undefined;
845
+ std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(v)), &bytes, v, end);
701
846
  return self.writeAll(&bytes);
702
847
  }
703
848
  };
@@ -717,10 +862,10 @@ pub const ByteStream = struct {
717
862
  }
718
863
  };
719
864
 
720
- pub const Kind = enum { Move, Species, Type, Status };
865
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
721
866
 
722
867
  pub fn format(
723
- comptime gen: anytype,
868
+ gen: anytype,
724
869
  a: []const u8,
725
870
  b: ?[]const u8,
726
871
  color: bool,
@@ -762,8 +907,18 @@ pub fn format(
762
907
  .Resisted => "|-resisted|",
763
908
  .Immune => "|-immune|",
764
909
  .Transform => "|-transform|",
910
+ .Drag => "|drag|",
911
+ .Item => "|-item|",
912
+ .EndItem => "|-enditem|",
913
+ .CureTeam => "|-cureteam|",
914
+ .SetHP => "|-sethp|",
915
+ .SetBoost => "|-setboost|",
916
+ .CopyBoost => "|-copyboost|",
765
917
  .SideStart => "|-sidestart|",
766
918
  .SideEnd => "|-sideend|",
919
+ .SingleMove => "|-singlemove|",
920
+ .SingleTurn => "|-singleturn|",
921
+ .Weather => "|-weather|",
767
922
  else => unreachable,
768
923
  };
769
924
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -788,7 +943,7 @@ pub fn format(
788
943
  printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
789
944
  }
790
945
  },
791
- .Switch => {
946
+ .Switch, .Drag => {
792
947
  const id = ID.from(@intCast(a[i]));
793
948
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
794
949
  printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
@@ -796,19 +951,19 @@ pub fn format(
796
951
  if (a[i] == N(gen.Gender.Unknown)) {
797
952
  i += 1;
798
953
  } else {
799
- var gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
954
+ const gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
800
955
  printc(", {s}", .{gender}, a, b, &i, 1, color);
801
956
  }
802
957
  }
803
958
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
804
959
  switch (endian) {
805
- .Big => {
960
+ big => {
806
961
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
807
962
  printc(" {d}", .{hp}, a, b, &i, 2, color);
808
963
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
809
964
  printc("/{d}", .{hp}, a, b, &i, 2, color);
810
965
  },
811
- .Little => {
966
+ little => {
812
967
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
813
968
  printc(" {d}", .{hp}, a, b, &i, 2, color);
814
969
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
@@ -832,14 +987,15 @@ pub fn format(
832
987
  .Crit,
833
988
  .SuperEffective,
834
989
  .Resisted,
990
+ .CureTeam,
835
991
  => {
836
992
  const id = ID.from(@intCast(a[i]));
837
993
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
838
994
  },
839
995
  .Turn => {
840
996
  const turn = switch (endian) {
841
- .Big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
842
- .Little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
997
+ big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
998
+ little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
843
999
  };
844
1000
  printc(" {d}", .{turn}, a, b, &i, 2, color);
845
1001
  },
@@ -850,13 +1006,13 @@ pub fn format(
850
1006
  var id = ID.from(@intCast(a[i]));
851
1007
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
852
1008
  switch (endian) {
853
- .Big => {
1009
+ big => {
854
1010
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
855
1011
  printc(" {d}", .{hp}, a, b, &i, 2, color);
856
1012
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
857
1013
  printc("/{d}", .{hp}, a, b, &i, 2, color);
858
1014
  },
859
- .Little => {
1015
+ little => {
860
1016
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
861
1017
  printc(" {d}", .{hp}, a, b, &i, 2, color);
862
1018
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
@@ -908,12 +1064,12 @@ pub fn format(
908
1064
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
909
1065
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
910
1066
  },
911
- .HitCount => {
1067
+ .HitCount, .SetBoost => {
912
1068
  const id = ID.from(@intCast(a[i]));
913
1069
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
914
1070
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
915
1071
  },
916
- .Prepare => {
1072
+ .Prepare, .SingleMove, .SingleTurn => {
917
1073
  const id = ID.from(@intCast(a[i]));
918
1074
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
919
1075
  printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
@@ -921,7 +1077,13 @@ pub fn format(
921
1077
  .Activate => {
922
1078
  const id = ID.from(@intCast(a[i]));
923
1079
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
924
- printc(" {s}", .{@tagName(@as(Activate, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1080
+ const reason: Activate = @enumFromInt(a[i]);
1081
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1082
+ if (reason == .SubstituteBlock) {
1083
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1084
+ } else if (reason == .Magnitude) {
1085
+ printc(" {d}", .{a[i]}, a, b, &i, 1, color);
1086
+ }
925
1087
  },
926
1088
  .Start => {
927
1089
  const id = ID.from(@intCast(a[i]));
@@ -929,6 +1091,7 @@ pub fn format(
929
1091
  const reason = a[i];
930
1092
  printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
931
1093
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
1094
+ // FIXME
932
1095
  const types = @as(gen1.Types, @bitCast(a[i]));
933
1096
  const args = .{
934
1097
  formatter(gen, .Type, @intFromEnum(types.type1)),
@@ -949,21 +1112,53 @@ pub fn format(
949
1112
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
950
1113
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
951
1114
  },
952
- .Transform => {
1115
+ .Transform, .CopyBoost => {
953
1116
  const source = ID.from(@intCast(a[i]));
954
1117
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
955
1118
  const target = ID.from(@intCast(a[i]));
956
1119
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
957
1120
  },
1121
+ .Item, .EndItem => {
1122
+ var id = ID.from(@intCast(a[i]));
1123
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1124
+ printc(" {s}", .{formatter(gen, .Item, a[i])}, a, b, &i, 1, color);
1125
+ if (arg == .Item) {
1126
+ id = ID.from(@intCast(a[i]));
1127
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1128
+ } else {
1129
+ const reason: EndItem = @enumFromInt(a[i]);
1130
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1131
+ }
1132
+ },
1133
+ .SetHP => {
1134
+ const id = ID.from(@intCast(a[i]));
1135
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1136
+ switch (endian) {
1137
+ big => {
1138
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1139
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1140
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1141
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1142
+ },
1143
+ little => {
1144
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1145
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1146
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1147
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1148
+ },
1149
+ }
1150
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1151
+ printc(" {s}", .{@tagName(@as(SetHP, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1152
+ },
958
1153
  .SideStart => {
959
1154
  const id = ID.from(@intCast(a[i]));
960
1155
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
961
- printc(" {s}", .{@tagName(@as(SideStart, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1156
+ printc(" {s}", .{@tagName(@as(Side, @enumFromInt(a[i])))}, a, b, &i, 1, color);
962
1157
  },
963
1158
  .SideEnd => {
964
1159
  var id = ID.from(@intCast(a[i]));
965
1160
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
966
- const reason: SideEnd = @enumFromInt(a[i]);
1161
+ const reason: Side = @enumFromInt(a[i]);
967
1162
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
968
1163
  if (reason == .Spikes) {
969
1164
  printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
@@ -971,6 +1166,10 @@ pub fn format(
971
1166
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
972
1167
  }
973
1168
  },
1169
+ .Weather => {
1170
+ printc(" {s}", .{formatter(gen, .Weather, a[i])}, a, b, &i, 1, color);
1171
+ printc(" {s}", .{@tagName(@as(Weather, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1172
+ },
974
1173
  else => unreachable,
975
1174
  }
976
1175
  print("\n", .{});
@@ -1009,14 +1208,11 @@ pub fn expectLog(
1009
1208
  ) !void {
1010
1209
  if (!enabled) return;
1011
1210
 
1012
- const color = color: {
1013
- if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
1014
- break :color true;
1015
- } else if (std.process.hasEnvVarConstant("NO_COLOR")) {
1016
- break :color false;
1017
- } else {
1018
- break :color std.io.getStdErr().supportsAnsiEscapeCodes();
1019
- }
1211
+ const color = if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR"))
1212
+ true
1213
+ else switch (std.io.tty.detectConfig(std.io.getStdErr())) {
1214
+ .escape_codes => true,
1215
+ else => false,
1020
1216
  };
1021
1217
 
1022
1218
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
@@ -1048,8 +1244,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
1048
1244
  }
1049
1245
  }
1050
1246
 
1051
- const endian = builtin.target.cpu.arch.endian();
1052
-
1053
1247
  fn N(e: anytype) u8 {
1054
1248
  return @intFromEnum(e);
1055
1249
  }
@@ -1057,22 +1251,20 @@ fn N(e: anytype) u8 {
1057
1251
  const p1 = Player.P1;
1058
1252
  const p2 = Player.P2;
1059
1253
 
1060
- const gen1 = struct {
1061
- pub usingnamespace @import("../gen1/data.zig");
1062
- pub const helpers = @import("../gen1/helpers.zig");
1063
- };
1064
-
1065
- const gen2 = struct {
1066
- pub usingnamespace @import("../gen2/data.zig");
1067
- pub const helpers = @import("../gen2/helpers.zig");
1068
- };
1069
-
1070
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
1254
+ var buf: [pkmn.LOGS_SIZE]u8 = undefined;
1071
1255
  var stream: ByteStream = .{ .buffer = &buf };
1072
1256
  var log: FixedLog = .{ .writer = stream.writer() };
1073
1257
 
1074
- const M = gen1.Move;
1075
- const S = gen1.Species;
1258
+ const gen1 = pkmn.gen1;
1259
+ const gen2 = pkmn.gen2;
1260
+
1261
+ const M1 = gen1.Move;
1262
+ const S1 = gen1.Species;
1263
+
1264
+ const M2 = gen2.Move;
1265
+ const S2 = gen2.Species;
1266
+ const I2 = gen2.Item;
1267
+ const W2 = gen2.Weather;
1076
1268
 
1077
1269
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
1078
1270
  return expectLog(gen1, expected, actual, 0);
@@ -1088,32 +1280,39 @@ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
1088
1280
  .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1089
1281
  .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1090
1282
  .Status => gen.Status.name(byte),
1283
+ .Item => if (@hasDecl(gen, "Item"))
1284
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1285
+ else
1286
+ unreachable,
1287
+ .Weather => if (@hasDecl(gen, "Weather"))
1288
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1289
+ else
1290
+ unreachable,
1091
1291
  };
1092
1292
  }
1093
1293
 
1094
1294
  test "|move|" {
1095
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1295
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
1096
1296
  try expectLog1(
1097
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1297
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
1098
1298
  buf[0..5],
1099
1299
  );
1100
1300
  stream.reset();
1101
1301
 
1102
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
1103
- const wrap = N(M.Wrap);
1302
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
1104
1303
  try expectLog1(
1105
- &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
1304
+ &.{ N(ArgType.Move), 0b1100, N(M1.Pound), 0b0101, N(Move.From), N(M1.Metronome) },
1106
1305
  buf[0..6],
1107
1306
  );
1108
1307
  stream.reset();
1109
1308
 
1110
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1111
- try log.laststill();
1309
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1310
+ try log.laststill(.{});
1112
1311
  try expectLog1(
1113
1312
  &.{
1114
1313
  N(ArgType.Move),
1115
1314
  0b1100,
1116
- N(M.SkullBash),
1315
+ N(M1.SkullBash),
1117
1316
  0,
1118
1317
  N(Move.None),
1119
1318
  N(ArgType.LastStill),
@@ -1122,13 +1321,13 @@ test "|move|" {
1122
1321
  );
1123
1322
  stream.reset();
1124
1323
 
1125
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1126
- try log.lastmiss();
1324
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1325
+ try log.lastmiss(.{});
1127
1326
  try expectLog1(
1128
1327
  &.{
1129
1328
  N(ArgType.Move),
1130
1329
  0b1100,
1131
- N(M.Tackle),
1330
+ N(M1.Tackle),
1132
1331
  0b0101,
1133
1332
  N(Move.None),
1134
1333
  N(ArgType.LastMiss),
@@ -1144,11 +1343,11 @@ test "|switch|" {
1144
1343
  snorlax.hp = 200;
1145
1344
  snorlax.stats.hp = 400;
1146
1345
  snorlax.status = gen1.Status.init(.PAR);
1147
- try log.switched(p2.ident(3), &snorlax);
1346
+ try log.switched(.{ p2.ident(3), &snorlax });
1148
1347
  const par = 0b1000000;
1149
1348
  var expected: []const u8 = switch (endian) {
1150
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
1151
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 200, 0, 144, 1, par },
1349
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1350
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1152
1351
  };
1153
1352
  try expectLog1(expected, buf[0..9]);
1154
1353
  stream.reset();
@@ -1156,19 +1355,19 @@ test "|switch|" {
1156
1355
  snorlax.level = 100;
1157
1356
  snorlax.hp = 0;
1158
1357
  snorlax.status = 0;
1159
- try log.switched(p2.ident(3), &snorlax);
1358
+ try log.switched(.{ p2.ident(3), &snorlax });
1160
1359
  expected = switch (endian) {
1161
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
1162
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 144, 1, 0 },
1360
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1361
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1163
1362
  };
1164
1363
  try expectLog1(expected, buf[0..9]);
1165
1364
  stream.reset();
1166
1365
 
1167
1366
  snorlax.hp = 400;
1168
- try log.switched(p2.ident(3), &snorlax);
1367
+ try log.switched(.{ p2.ident(3), &snorlax });
1169
1368
  expected = switch (endian) {
1170
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
1171
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 144, 1, 144, 1, 0 },
1369
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1370
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1172
1371
  };
1173
1372
  try expectLog1(expected, buf[0..9]);
1174
1373
  stream.reset();
@@ -1178,59 +1377,59 @@ test "|switch|" {
1178
1377
  blissey.hp = 200;
1179
1378
  blissey.stats.hp = 400;
1180
1379
  blissey.status = gen2.Status.init(.PAR);
1181
- try log.switched(p2.ident(3), &blissey);
1380
+ try log.switched(.{ p2.ident(3), &blissey });
1182
1381
  expected = &(.{
1183
1382
  N(ArgType.Switch),
1184
1383
  0b1011,
1185
- N(gen2.Species.Blissey),
1384
+ N(S2.Blissey),
1186
1385
  N(gen2.Gender.Female),
1187
1386
  91,
1188
1387
  } ++ switch (endian) {
1189
- .Big => .{ 0, 200, 1, 144, par },
1190
- .Little => .{ 200, 0, 144, 1, par },
1388
+ big => .{ 0, 200, 1, 144, par },
1389
+ little => .{ 200, 0, 144, 1, par },
1191
1390
  });
1192
1391
  try expectLog2(expected, buf[0..10]);
1193
1392
  stream.reset();
1194
1393
  }
1195
1394
 
1196
1395
  test "|cant|" {
1197
- try log.cant(p2.ident(6), .Bound);
1396
+ try log.cant(.{ p2.ident(6), .Bound });
1198
1397
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1199
1398
  stream.reset();
1200
1399
 
1201
- try log.disabled(p1.ident(2), M.Earthquake);
1202
- try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M.Earthquake) }, buf[0..4]);
1400
+ try log.cant(.{ p1.ident(2), .Disable, M1.Earthquake });
1401
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1203
1402
  stream.reset();
1204
1403
  }
1205
1404
 
1206
1405
  test "|faint|" {
1207
- try log.faint(p2.ident(2), false);
1406
+ try log.faint(.{ p2.ident(2), false });
1208
1407
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1209
1408
  stream.reset();
1210
1409
 
1211
- try log.faint(p2.ident(2), true);
1410
+ try log.faint(.{ p2.ident(2), true });
1212
1411
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1213
1412
  stream.reset();
1214
1413
  }
1215
1414
 
1216
1415
  test "|turn|" {
1217
- try log.turn(42);
1218
- var expected = switch (endian) {
1219
- .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1220
- .Little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1416
+ try log.turn(.{42});
1417
+ const expected = switch (endian) {
1418
+ big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1419
+ little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1221
1420
  };
1222
1421
  try expectLog1(expected, buf[0..4]);
1223
1422
  stream.reset();
1224
1423
  }
1225
1424
 
1226
1425
  test "|win|" {
1227
- try log.win(.P2);
1426
+ try log.win(.{.P2});
1228
1427
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1229
1428
  stream.reset();
1230
1429
  }
1231
1430
 
1232
1431
  test "|tie|" {
1233
- try log.tie();
1432
+ try log.tie(.{});
1234
1433
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1235
1434
  stream.reset();
1236
1435
  }
@@ -1239,10 +1438,10 @@ test "|-damage|" {
1239
1438
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1240
1439
  chansey.hp = 612;
1241
1440
  chansey.status = gen1.Status.slp(1);
1242
- try log.damage(p2.ident(2), &chansey, .None);
1441
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1243
1442
  var expected: []const u8 = switch (endian) {
1244
- .Big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1245
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1443
+ big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1444
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1246
1445
  };
1247
1446
  try expectLog1(expected, buf[0..8]);
1248
1447
  stream.reset();
@@ -1250,19 +1449,19 @@ test "|-damage|" {
1250
1449
  chansey.hp = 100;
1251
1450
  chansey.stats.hp = 256;
1252
1451
  chansey.status = 0;
1253
- try log.damage(p2.ident(2), &chansey, .Confusion);
1452
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1254
1453
  expected = switch (endian) {
1255
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1256
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1454
+ big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1455
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1257
1456
  };
1258
1457
  try expectLog1(expected, buf[0..8]);
1259
1458
  stream.reset();
1260
1459
 
1261
1460
  chansey.status = gen1.Status.init(.PSN);
1262
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1461
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1263
1462
  expected = switch (endian) {
1264
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1265
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1463
+ big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1464
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1266
1465
  };
1267
1466
  try expectLog1(expected, buf[0..9]);
1268
1467
  stream.reset();
@@ -1272,10 +1471,10 @@ test "|-heal|" {
1272
1471
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1273
1472
  chansey.hp = 612;
1274
1473
  chansey.status = gen1.Status.slp(1);
1275
- try log.heal(p2.ident(2), &chansey, .None);
1474
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1276
1475
  var expected: []const u8 = switch (endian) {
1277
- .Big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1278
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1476
+ big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1477
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1279
1478
  };
1280
1479
  try expectLog1(expected, buf[0..8]);
1281
1480
  stream.reset();
@@ -1283,46 +1482,46 @@ test "|-heal|" {
1283
1482
  chansey.hp = 100;
1284
1483
  chansey.stats.hp = 256;
1285
1484
  chansey.status = 0;
1286
- try log.heal(p2.ident(2), &chansey, .Silent);
1485
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1287
1486
  expected = switch (endian) {
1288
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1289
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1487
+ big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1488
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1290
1489
  };
1291
1490
  try expectLog1(expected, buf[0..8]);
1292
1491
  stream.reset();
1293
1492
 
1294
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1493
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1295
1494
  expected = switch (endian) {
1296
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1297
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1495
+ big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1496
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1298
1497
  };
1299
1498
  try expectLog1(expected, buf[0..9]);
1300
1499
  stream.reset();
1301
1500
  }
1302
1501
 
1303
1502
  test "|-status|" {
1304
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1503
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1305
1504
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1306
1505
  stream.reset();
1307
1506
 
1308
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1507
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1309
1508
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1310
1509
  stream.reset();
1311
1510
 
1312
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1511
+ try log.status(.{ p1.ident(1), gen1.Status.init(.PAR), .From, M1.BodySlam });
1313
1512
  try expectLog1(
1314
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1513
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1315
1514
  buf[0..5],
1316
1515
  );
1317
1516
  stream.reset();
1318
1517
  }
1319
1518
 
1320
1519
  test "|-curestatus|" {
1321
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1520
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1322
1521
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1323
1522
  stream.reset();
1324
1523
 
1325
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1524
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1326
1525
  try expectLog1(&.{
1327
1526
  N(ArgType.CureStatus),
1328
1527
  0b0010,
@@ -1333,183 +1532,306 @@ test "|-curestatus|" {
1333
1532
  }
1334
1533
 
1335
1534
  test "|-boost|" {
1336
- try log.boost(p2.ident(6), .Speed, 2);
1535
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1337
1536
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1338
1537
  stream.reset();
1339
1538
 
1340
- try log.boost(p1.ident(2), .Rage, 1);
1539
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1341
1540
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1342
1541
  stream.reset();
1343
1542
 
1344
- try log.boost(p2.ident(3), .Defense, -2);
1543
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1345
1544
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1346
1545
  stream.reset();
1347
1546
  }
1348
1547
 
1349
1548
  test "|-clearallboost|" {
1350
- try log.clearallboost();
1549
+ try log.clearallboost(.{});
1351
1550
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1352
1551
  stream.reset();
1353
1552
  }
1354
1553
 
1355
1554
  test "|-fail|" {
1356
- try log.fail(p2.ident(6), .None);
1555
+ try log.fail(.{ p2.ident(6), .None });
1357
1556
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1358
1557
  stream.reset();
1359
1558
 
1360
- try log.fail(p2.ident(6), .Sleep);
1559
+ try log.fail(.{ p2.ident(6), .Sleep });
1361
1560
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1362
1561
  stream.reset();
1363
1562
 
1364
- try log.fail(p2.ident(6), .Substitute);
1563
+ try log.fail(.{ p2.ident(6), .Substitute });
1365
1564
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1366
1565
  stream.reset();
1367
1566
 
1368
- try log.fail(p2.ident(6), .Weak);
1567
+ try log.fail(.{ p2.ident(6), .Weak });
1369
1568
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1370
1569
  stream.reset();
1371
1570
  }
1372
1571
 
1373
1572
  test "|-miss|" {
1374
- try log.miss(p2.ident(4));
1573
+ try log.miss(.{p2.ident(4)});
1375
1574
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1376
1575
  stream.reset();
1377
1576
  }
1378
1577
  test "|-hitcount|" {
1379
- try log.hitcount(p2.ident(1), 5);
1578
+ try log.hitcount(.{ p2.ident(1), 5 });
1380
1579
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1381
1580
  stream.reset();
1382
1581
  }
1383
1582
 
1384
1583
  test "|-prepare|" {
1385
- try log.prepare(p2.ident(2), M.Dig);
1386
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1584
+ try log.prepare(.{ p2.ident(2), M1.Dig });
1585
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1387
1586
  stream.reset();
1388
1587
  }
1389
1588
 
1390
1589
  test "|-mustrecharge|" {
1391
- try log.mustrecharge(p1.ident(6));
1590
+ try log.mustrecharge(.{p1.ident(6)});
1392
1591
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1393
1592
  stream.reset();
1394
1593
  }
1395
1594
 
1396
1595
  test "|-activate|" {
1397
- try log.activate(p1.ident(2), .Struggle);
1596
+ try log.activate(.{ p1.ident(2), .Struggle });
1398
1597
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1399
1598
  stream.reset();
1400
1599
 
1401
- try log.activate(p2.ident(4), .Mist);
1600
+ try log.activate(.{ p2.ident(4), .Mist });
1402
1601
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1403
1602
  stream.reset();
1404
1603
 
1405
- try log.activate(p2.ident(6), .Substitute);
1604
+ try log.activate(.{ p2.ident(6), .Substitute });
1406
1605
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1407
1606
  stream.reset();
1408
1607
 
1409
- try log.activate(p1.ident(2), .Splash);
1608
+ try log.activate(.{ p1.ident(2), .Splash });
1410
1609
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1411
1610
  stream.reset();
1611
+
1612
+ try log.activate(.{ p1.ident(4), .SubstituteBlock, M2.Spite });
1613
+ try expectLog1(&.{
1614
+ N(ArgType.Activate),
1615
+ 0b0100,
1616
+ N(Activate.SubstituteBlock),
1617
+ N(M2.Spite),
1618
+ }, buf[0..4]);
1619
+ stream.reset();
1620
+
1621
+ try log.activate(.{ p1.ident(3), .Magnitude, 5 });
1622
+ try expectLog1(&.{ N(ArgType.Activate), 0b0011, N(Activate.Magnitude), 5 }, buf[0..4]);
1623
+ stream.reset();
1412
1624
  }
1413
1625
 
1414
1626
  test "|-fieldactivate|" {
1415
- try log.fieldactivate();
1627
+ try log.fieldactivate(.{});
1416
1628
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1417
1629
  stream.reset();
1418
1630
  }
1419
1631
 
1420
1632
  test "|-start|" {
1421
- try log.start(p2.ident(6), .Bide);
1633
+ try log.start(.{ p2.ident(6), .Bide });
1422
1634
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1423
1635
  stream.reset();
1424
1636
 
1425
- try log.start(p1.ident(2), .ConfusionSilent);
1637
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1426
1638
  try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1427
1639
  stream.reset();
1428
1640
 
1429
- try log.typechange(p2.ident(6), gen1.Types{ .type1 = .Fire, .type2 = .Fire }, p2.ident(5));
1641
+ try log.start(.{
1642
+ p2.ident(6),
1643
+ .TypeChange,
1644
+ gen1.Types{ .type1 = .Fire, .type2 = .Fire },
1645
+ p2.ident(5),
1646
+ });
1430
1647
  try expectLog1(
1431
1648
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1432
1649
  buf[0..5],
1433
1650
  );
1434
1651
  stream.reset();
1435
1652
 
1436
- try log.typechange(p1.ident(2), gen1.Types{ .type1 = .Bug, .type2 = .Poison }, p2.ident(4));
1653
+ try log.start(.{
1654
+ p1.ident(2),
1655
+ .TypeChange,
1656
+ gen1.Types{ .type1 = .Bug, .type2 = .Poison },
1657
+ p2.ident(4),
1658
+ });
1437
1659
  try expectLog1(
1438
1660
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1439
1661
  buf[0..5],
1440
1662
  );
1441
1663
  stream.reset();
1442
1664
 
1443
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1444
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M.Surf) }, buf[0..4]);
1665
+ try log.start(.{ p1.ident(2), .Disable, M1.Surf });
1666
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1445
1667
  stream.reset();
1446
1668
 
1447
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1448
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M.Surf) }, buf[0..4]);
1669
+ try log.start(.{ p1.ident(2), .Mimic, M1.Surf });
1670
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1449
1671
  stream.reset();
1450
1672
  }
1451
1673
 
1452
1674
  test "|-end|" {
1453
- try log.end(p2.ident(6), .Bide);
1675
+ try log.end(.{ p2.ident(6), .Bide });
1454
1676
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1455
1677
  stream.reset();
1456
1678
 
1457
- try log.end(p1.ident(2), .ConfusionSilent);
1679
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1458
1680
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1459
1681
  stream.reset();
1460
1682
  }
1461
1683
 
1462
1684
  test "|-ohko|" {
1463
- try log.ohko();
1685
+ try log.ohko(.{});
1464
1686
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1465
1687
  stream.reset();
1466
1688
  }
1689
+
1467
1690
  test "|-crit|" {
1468
- try log.crit(p2.ident(5));
1691
+ try log.crit(.{p2.ident(5)});
1469
1692
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1470
1693
  stream.reset();
1471
1694
  }
1695
+
1472
1696
  test "|-supereffective|" {
1473
- try log.supereffective(p1.ident(1));
1697
+ try log.supereffective(.{p1.ident(1)});
1474
1698
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1475
1699
  stream.reset();
1476
1700
  }
1701
+
1477
1702
  test "|-resisted|" {
1478
- try log.resisted(p2.ident(2));
1703
+ try log.resisted(.{p2.ident(2)});
1479
1704
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1480
1705
  stream.reset();
1481
1706
  }
1707
+
1482
1708
  test "|-immune|" {
1483
- try log.immune(p1.ident(3), .None);
1709
+ try log.immune(.{ p1.ident(3), .None });
1484
1710
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1485
1711
  stream.reset();
1486
1712
 
1487
- try log.immune(p2.ident(2), .OHKO);
1713
+ try log.immune(.{ p2.ident(2), .OHKO });
1488
1714
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1489
1715
  stream.reset();
1490
1716
  }
1717
+
1491
1718
  test "|-transform|" {
1492
- try log.transform(p2.ident(4), p1.ident(5));
1719
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1493
1720
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1494
1721
  stream.reset();
1495
1722
  }
1723
+
1724
+ test "|drag|" {
1725
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1726
+ blissey.level = 91;
1727
+ blissey.hp = 200;
1728
+ blissey.stats.hp = 400;
1729
+ blissey.status = gen2.Status.init(.PAR);
1730
+ try log.drag(.{ p2.ident(3), &blissey });
1731
+ const par = 0b1000000;
1732
+ const expected = &(.{
1733
+ N(ArgType.Drag),
1734
+ 0b1011,
1735
+ N(S2.Blissey),
1736
+ N(gen2.Gender.Female),
1737
+ 91,
1738
+ } ++ switch (endian) {
1739
+ big => .{ 0, 200, 1, 144, par },
1740
+ little => .{ 200, 0, 144, 1, par },
1741
+ });
1742
+ try expectLog2(expected, buf[0..10]);
1743
+ stream.reset();
1744
+ }
1745
+
1746
+ test "|-item|" {
1747
+ try log.item(.{ p2.ident(2), I2.GoldBerry, p1.ident(4) });
1748
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1749
+ stream.reset();
1750
+ }
1751
+
1752
+ test "|-enditem|" {
1753
+ try log.enditem(.{ p1.ident(1), I2.BerserkGene, .None });
1754
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1755
+ stream.reset();
1756
+
1757
+ try log.enditem(.{ p2.ident(4), I2.PRZCureBerry, .Eat });
1758
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1759
+ stream.reset();
1760
+ }
1761
+
1762
+ test "|-cureteam|" {
1763
+ try log.cureteam(.{p2.ident(5)});
1764
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1765
+ stream.reset();
1766
+ }
1767
+
1768
+ test "|-sethp|" {
1769
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1770
+ blissey.hp = 612;
1771
+ try log.sethp(.{ p2.ident(2), &blissey, .Silent });
1772
+ const expected: []const u8 = switch (endian) {
1773
+ big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1774
+ little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1775
+ };
1776
+ try expectLog2(expected, buf[0..8]);
1777
+ stream.reset();
1778
+ }
1779
+
1780
+ test "|-setboost|" {
1781
+ try log.setboost(.{ p2.ident(6), 6 });
1782
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1783
+ stream.reset();
1784
+
1785
+ try log.setboost(.{ p2.ident(3), 2 });
1786
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1787
+ stream.reset();
1788
+ }
1789
+
1790
+ test "|-copyboost|" {
1791
+ try log.copyboost(.{ p2.ident(3), p1.ident(6) });
1792
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1793
+ stream.reset();
1794
+ }
1795
+
1496
1796
  test "|-sidestart|" {
1497
- try log.sidestart(p2.ident(6), .Reflect);
1498
- try expectLog2(&.{ N(ArgType.SideStart), 0b1110, N(SideStart.Reflect) }, buf[0..3]);
1797
+ try log.sidestart(.{ .P2, .Reflect });
1798
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1499
1799
  stream.reset();
1500
1800
  }
1801
+
1501
1802
  test "|-sideend|" {
1502
- try log.sideend(p2.ident(6), .LightScreen);
1503
- try expectLog2(&.{ N(ArgType.SideEnd), 0b1110, N(SideEnd.LightScreen) }, buf[0..3]);
1803
+ try log.sideend(.{ .P2, .LightScreen });
1804
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1504
1805
  stream.reset();
1505
1806
 
1506
- try log.sideendFromOf(p1.ident(2), .Spikes, gen2.Move.RapidSpin, p1.ident(3));
1807
+ try log.sideend(.{ .P1, .Spikes, p1.ident(3) });
1507
1808
  try expectLog2(&.{
1508
1809
  N(ArgType.SideEnd),
1509
- 0b0010,
1510
- N(SideEnd.Spikes),
1511
- N(gen2.Move.RapidSpin),
1810
+ 0,
1811
+ N(Side.Spikes),
1512
1812
  0b0011,
1513
- }, buf[0..5]);
1813
+ }, buf[0..4]);
1814
+ stream.reset();
1815
+ }
1816
+
1817
+ test "|-singlemove|" {
1818
+ try log.singlemove(.{ p2.ident(2), M2.DestinyBond });
1819
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1820
+ stream.reset();
1821
+ }
1822
+
1823
+ test "|-singleturn|" {
1824
+ try log.singleturn(.{ p1.ident(3), M2.Protect });
1825
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1826
+ stream.reset();
1827
+ }
1828
+
1829
+ test "|-weather|" {
1830
+ try log.weather(.{ W2.Rain, .Upkeep });
1831
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1832
+ stream.reset();
1833
+
1834
+ try log.weather(.{ W2.None, .None });
1835
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1514
1836
  stream.reset();
1515
1837
  }