@pkmn/engine 0.1.0-dev.a324c50f → 0.1.0-dev.a77ab8e2

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