@pkmn/engine 0.1.0-dev.a02611a4 → 0.1.0-dev.a1fcc5d9

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