@pkmn/engine 0.1.0-dev.8b83af6b → 0.1.0-dev.8cf7e679

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