@pkmn/engine 0.1.0-dev.736f7a8b → 0.1.0-dev.73fa6bda

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