@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.cdc837af

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