@pkmn/engine 0.1.0-dev.1220fbc5 → 0.1.0-dev.130de754

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