@pkmn/engine 0.1.0-dev.d56bdadd → 0.1.0-dev.d73cd76c

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