@pkmn/engine 0.1.0-dev.44a51703 → 0.1.0-dev.44c4a618

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