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

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