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

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 +63 -26
  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 +927 -478
  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 +662 -583
  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 +114 -96
  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 -115
  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 -308
  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
+ };
231
294
 
232
- /// Logs protocol information to its `Writer` during a battle update when `options.log` is enabled.
233
- pub fn Log(comptime Writer: type) type {
295
+ pub const SetHP = enum(u8) {
296
+ None,
297
+ Silent,
298
+ };
299
+
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,
318
+
319
+ pub const Error = err(WriterT);
238
320
 
239
- pub const Error = Writer.Error;
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])));
586
774
  }
587
775
 
588
- pub fn transform(self: Self, source: ID, target: ID) Error!void {
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);
784
+ }
785
+
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]),
794
+ });
795
+ }
796
+
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])),
595
806
  });
596
807
  }
597
808
 
598
- pub fn laststill(self: Self) Error!void {
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,58 +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 = .{} };
642
866
 
643
- pub fn writer(self: *ByteStream) Writer {
644
- return .{ .stream = self };
867
+ const NullWriter = struct {
868
+ pub const Error = error{};
869
+
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 };
659
- pub const Formatter = fn (Kind, u8) []const u8;
883
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
660
884
 
661
885
  pub fn format(
662
- comptime gen: comptime_int,
663
- comptime formatter: Formatter,
886
+ gen: anytype,
664
887
  a: []const u8,
665
888
  b: ?[]const u8,
666
889
  color: bool,
@@ -702,6 +925,18 @@ pub fn format(
702
925
  .Resisted => "|-resisted|",
703
926
  .Immune => "|-immune|",
704
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|",
705
940
  else => unreachable,
706
941
  };
707
942
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -715,51 +950,53 @@ pub fn format(
715
950
  .OHKO,
716
951
  => {},
717
952
  .Move => {
718
- const source = ID.from(@intCast(a[i]));
953
+ const source: ID = .from(@intCast(a[i]));
719
954
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
720
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
721
- const target = ID.from(@intCast(a[i]));
955
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
956
+ const target: ID = .from(@intCast(a[i]));
722
957
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
723
958
  const reason: Move = @enumFromInt(a[i]);
724
959
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
725
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
960
+ if (reason == .From) {
961
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
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
- printc(" {s}", .{formatter(.Species, a[i])}, a, b, &i, 1, color);
731
- if (gen > 1) {
732
- if (a[i] == N(gen2.Gender.Unknown)) {
967
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
968
+ if (@hasDecl(gen, "Gender")) {
969
+ if (a[i] == N(gen.Gender.Unknown)) {
733
970
  i += 1;
734
971
  } else {
735
- var gender = if (a[i] == N(gen2.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]);
751
988
  printc("/{d}", .{hp}, a, b, &i, 2, color);
752
989
  },
753
990
  }
754
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
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);
761
998
  if (reason == .Disable) {
762
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
999
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
763
1000
  }
764
1001
  },
765
1002
  .Faint,
@@ -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,112 +1021,173 @@ 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]);
799
1037
  printc("/{d}", .{hp}, a, b, &i, 2, color);
800
1038
  },
801
1039
  }
802
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1040
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
803
1041
  if (arg == .Damage) {
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
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1060
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
823
1061
  const reason: Status = @enumFromInt(a[i]);
824
1062
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
825
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1063
+ if (reason == .From) {
1064
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1065
+ }
826
1066
  },
827
1067
  .CureStatus => {
828
- const id = ID.from(@intCast(a[i]));
1068
+ const id: ID = .from(@intCast(a[i]));
829
1069
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
830
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1070
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
831
1071
  const reason: CureStatus = @enumFromInt(a[i]);
832
1072
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
833
1073
  },
834
1074
  .Boost => {
835
- const id = ID.from(@intCast(a[i]));
1075
+ const id: ID = .from(@intCast(a[i]));
836
1076
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
837
1077
  printc(" {s}", .{@tagName(@as(Boost, @enumFromInt(a[i])))}, a, b, &i, 1, color);
838
1078
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
839
1079
  },
840
1080
  .Fail => {
841
- const id = ID.from(@intCast(a[i]));
1081
+ const id: ID = .from(@intCast(a[i]));
842
1082
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
843
1083
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
844
1084
  },
845
- .HitCount => {
846
- const id = ID.from(@intCast(a[i]));
1085
+ .HitCount, .SetBoost => {
1086
+ const id: ID = .from(@intCast(a[i]));
847
1087
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
848
1088
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
849
1089
  },
850
- .Prepare => {
851
- const id = ID.from(@intCast(a[i]));
1090
+ .Prepare, .SingleMove, .SingleTurn => {
1091
+ const id: ID = .from(@intCast(a[i]));
852
1092
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
853
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1093
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
854
1094
  },
855
1095
  .Activate => {
856
- const id = ID.from(@intCast(a[i]));
1096
+ const id: ID = .from(@intCast(a[i]));
857
1097
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
858
- printc(" {s}", .{@tagName(@as(Activate, @enumFromInt(a[i])))}, a, b, &i, 1, color);
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
+ }
859
1105
  },
860
1106
  .Start => {
861
- const id = ID.from(@intCast(a[i]));
1107
+ const id: ID = .from(@intCast(a[i]));
862
1108
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
863
1109
  const reason = a[i];
864
1110
  printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
865
1111
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
1112
+ // FIXME
866
1113
  const types = @as(gen1.Types, @bitCast(a[i]));
867
1114
  const args = .{
868
- formatter(.Type, @intFromEnum(types.type1)),
869
- formatter(.Type, @intFromEnum(types.type2)),
1115
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1116
+ formatter(gen, .Type, @intFromEnum(types.type2)),
870
1117
  };
871
1118
  printc(" {s}/{s}", args, a, b, &i, 1, color);
872
1119
  } else if (reason >= @intFromEnum(Start.Disable)) {
873
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1120
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
874
1121
  }
875
1122
  },
876
1123
  .End => {
877
- const id = ID.from(@intCast(a[i]));
1124
+ const id: ID = .from(@intCast(a[i]));
878
1125
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
879
1126
  printc(" {s}", .{@tagName(@as(End, @enumFromInt(a[i])))}, a, b, &i, 1, color);
880
1127
  },
881
1128
  .Immune => {
882
- const id = ID.from(@intCast(a[i]));
1129
+ const id: ID = .from(@intCast(a[i]));
883
1130
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
884
1131
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
885
1132
  },
886
- .Transform => {
887
- const source = ID.from(@intCast(a[i]));
1133
+ .Transform, .CopyBoost => {
1134
+ const source: ID = .from(@intCast(a[i]));
888
1135
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
889
- const target = ID.from(@intCast(a[i]));
1136
+ const target: ID = .from(@intCast(a[i]));
890
1137
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
891
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
+ },
892
1191
  else => unreachable,
893
1192
  }
894
1193
  print("\n", .{});
@@ -920,31 +1219,44 @@ fn printc(
920
1219
  }
921
1220
 
922
1221
  pub fn expectLog(
923
- comptime gen: comptime_int,
924
- comptime formatter: Formatter,
1222
+ comptime gen: anytype,
925
1223
  expected: []const u8,
926
1224
  actual: []const u8,
927
1225
  offset: usize,
928
1226
  ) !void {
929
1227
  if (!enabled) return;
930
1228
 
931
- const color = color: {
932
- if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
933
- break :color true;
934
- } else if (std.process.hasEnvVarConstant("NO_COLOR")) {
935
- break :color false;
936
- } else {
937
- break :color std.io.getStdErr().supportsAnsiEscapeCodes();
938
- }
939
- };
1229
+ const color = detect();
940
1230
 
941
1231
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
942
1232
  error.TestExpectedEqual => {
943
- format(gen, formatter, expected, null, color);
944
- format(gen, formatter, actual, expected, color);
1233
+ format(gen, expected, null, color);
1234
+ format(gen, actual, expected, color);
945
1235
  return err;
946
1236
  },
947
- 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,
948
1260
  };
949
1261
  }
950
1262
 
@@ -952,7 +1264,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
952
1264
  for (offset..@min(expected.len, actual.len)) |i| {
953
1265
  if (expected[i] != actual[i]) {
954
1266
  print(
955
- "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",
956
1268
  .{ i, expected[i], actual[i] },
957
1269
  );
958
1270
  return error.TestExpectedEqual;
@@ -967,8 +1279,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
967
1279
  }
968
1280
  }
969
1281
 
970
- const endian = builtin.target.cpu.arch.endian();
971
-
972
1282
  fn N(e: anytype) u8 {
973
1283
  return @intFromEnum(e);
974
1284
  }
@@ -976,94 +1286,90 @@ fn N(e: anytype) u8 {
976
1286
  const p1 = Player.P1;
977
1287
  const p2 = Player.P2;
978
1288
 
979
- const gen1 = struct {
980
- pub usingnamespace @import("../gen1/data.zig");
981
- pub const helpers = @import("../gen1/helpers.zig");
982
- };
1289
+ var buf: [pkmn.LOGS_SIZE]u8 = undefined;
1290
+ var writer: Writer = .{ .buffer = &buf };
1291
+ var log: FixedLog = .{ .writer = &writer };
983
1292
 
984
- const gen2 = struct {
985
- pub usingnamespace @import("../gen2/data.zig");
986
- pub const helpers = @import("../gen2/helpers.zig");
987
- };
1293
+ const gen1 = pkmn.gen1;
1294
+ const gen2 = pkmn.gen2;
988
1295
 
989
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
990
- var stream = ByteStream{ .buffer = &buf };
991
- var log: FixedLog = .{ .writer = stream.writer() };
1296
+ const M1 = gen1.Move;
1297
+ const S1 = gen1.Species;
992
1298
 
993
- const M = gen1.Move;
994
- const S = gen1.Species;
1299
+ const M2 = gen2.Move;
1300
+ const S2 = gen2.Species;
1301
+ const I2 = gen2.Item;
1302
+ const W2 = gen2.Weather;
995
1303
 
996
1304
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
997
- return expectLog(1, gen1Formatter, expected, actual, 0);
1305
+ return expectLog(gen1, expected, actual, 0);
998
1306
  }
999
1307
 
1000
1308
  fn expectLog2(expected: []const u8, actual: []const u8) !void {
1001
- return expectLog(2, gen2Formatter, expected, actual, 0);
1002
- }
1003
-
1004
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1005
- return switch (kind) {
1006
- .Move => @tagName(@as(gen1.Move, @enumFromInt(byte))),
1007
- .Species => @tagName(@as(gen1.Species, @enumFromInt(byte))),
1008
- .Type => @tagName(@as(gen1.Type, @enumFromInt(byte))),
1009
- .Status => gen1.Status.name(byte),
1010
- };
1309
+ return expectLog(gen2, expected, actual, 0);
1011
1310
  }
1012
1311
 
1013
- fn gen2Formatter(kind: Kind, byte: u8) []const u8 {
1312
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
1014
1313
  return switch (kind) {
1015
- .Move => @tagName(@as(gen2.Move, @enumFromInt(byte))),
1016
- .Species => @tagName(@as(gen2.Species, @enumFromInt(byte))),
1017
- .Type => @tagName(@as(gen2.Type, @enumFromInt(byte))),
1018
- .Status => gen2.Status.name(byte),
1314
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1315
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1316
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
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,
1019
1326
  };
1020
1327
  }
1021
1328
 
1022
1329
  test "|move|" {
1023
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1330
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
1024
1331
  try expectLog1(
1025
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1332
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
1026
1333
  buf[0..5],
1027
1334
  );
1028
- stream.reset();
1335
+ writer.reset();
1029
1336
 
1030
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
1031
- const wrap = N(M.Wrap);
1337
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
1032
1338
  try expectLog1(
1033
- &.{ 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) },
1034
1340
  buf[0..6],
1035
1341
  );
1036
- stream.reset();
1342
+ writer.reset();
1037
1343
 
1038
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1039
- try log.laststill();
1344
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1345
+ try log.laststill(.{});
1040
1346
  try expectLog1(
1041
1347
  &.{
1042
1348
  N(ArgType.Move),
1043
1349
  0b1100,
1044
- N(M.SkullBash),
1350
+ N(M1.SkullBash),
1045
1351
  0,
1046
1352
  N(Move.None),
1047
1353
  N(ArgType.LastStill),
1048
1354
  },
1049
1355
  buf[0..6],
1050
1356
  );
1051
- stream.reset();
1357
+ writer.reset();
1052
1358
 
1053
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1054
- try log.lastmiss();
1359
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1360
+ try log.lastmiss(.{});
1055
1361
  try expectLog1(
1056
1362
  &.{
1057
1363
  N(ArgType.Move),
1058
1364
  0b1100,
1059
- N(M.Tackle),
1365
+ N(M1.Tackle),
1060
1366
  0b0101,
1061
1367
  N(Move.None),
1062
1368
  N(ArgType.LastMiss),
1063
1369
  },
1064
1370
  buf[0..6],
1065
1371
  );
1066
- stream.reset();
1372
+ writer.reset();
1067
1373
  }
1068
1374
 
1069
1375
  test "|switch|" {
@@ -1072,352 +1378,495 @@ test "|switch|" {
1072
1378
  snorlax.hp = 200;
1073
1379
  snorlax.stats.hp = 400;
1074
1380
  snorlax.status = gen1.Status.init(.PAR);
1075
- try log.switched(p2.ident(3), &snorlax);
1381
+ try log.switched(.{ p2.ident(3), &snorlax });
1076
1382
  const par = 0b1000000;
1077
1383
  var expected: []const u8 = switch (endian) {
1078
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
1079
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 200, 0, 144, 1, par },
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 },
1080
1386
  };
1081
1387
  try expectLog1(expected, buf[0..9]);
1082
- stream.reset();
1388
+ writer.reset();
1083
1389
 
1084
1390
  snorlax.level = 100;
1085
1391
  snorlax.hp = 0;
1086
1392
  snorlax.status = 0;
1087
- try log.switched(p2.ident(3), &snorlax);
1393
+ try log.switched(.{ p2.ident(3), &snorlax });
1088
1394
  expected = switch (endian) {
1089
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
1090
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 144, 1, 0 },
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 },
1091
1397
  };
1092
1398
  try expectLog1(expected, buf[0..9]);
1093
- stream.reset();
1399
+ writer.reset();
1094
1400
 
1095
1401
  snorlax.hp = 400;
1096
- try log.switched(p2.ident(3), &snorlax);
1402
+ try log.switched(.{ p2.ident(3), &snorlax });
1097
1403
  expected = switch (endian) {
1098
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
1099
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 144, 1, 144, 1, 0 },
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 },
1100
1406
  };
1101
1407
  try expectLog1(expected, buf[0..9]);
1102
- stream.reset();
1408
+ writer.reset();
1103
1409
 
1104
1410
  var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1105
1411
  blissey.level = 91;
1106
1412
  blissey.hp = 200;
1107
1413
  blissey.stats.hp = 400;
1108
1414
  blissey.status = gen2.Status.init(.PAR);
1109
- try log.switched(p2.ident(3), &blissey);
1415
+ try log.switched(.{ p2.ident(3), &blissey });
1110
1416
  expected = &(.{
1111
1417
  N(ArgType.Switch),
1112
1418
  0b1011,
1113
- N(gen2.Species.Blissey),
1419
+ N(S2.Blissey),
1114
1420
  N(gen2.Gender.Female),
1115
1421
  91,
1116
1422
  } ++ switch (endian) {
1117
- .Big => .{ 0, 200, 1, 144, par },
1118
- .Little => .{ 200, 0, 144, 1, par },
1423
+ .big => .{ 0, 200, 1, 144, par },
1424
+ .little => .{ 200, 0, 144, 1, par },
1119
1425
  });
1120
1426
  try expectLog2(expected, buf[0..10]);
1121
- stream.reset();
1427
+ writer.reset();
1122
1428
  }
1123
1429
 
1124
1430
  test "|cant|" {
1125
- try log.cant(p2.ident(6), .Bound);
1431
+ try log.cant(.{ p2.ident(6), .Bound });
1126
1432
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1127
- stream.reset();
1433
+ writer.reset();
1128
1434
 
1129
- try log.disabled(p1.ident(2), M.Earthquake);
1130
- try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M.Earthquake) }, buf[0..4]);
1131
- 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();
1132
1438
  }
1133
1439
 
1134
1440
  test "|faint|" {
1135
- try log.faint(p2.ident(2), false);
1441
+ try log.faint(.{ p2.ident(2), false });
1136
1442
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1137
- stream.reset();
1443
+ writer.reset();
1138
1444
 
1139
- try log.faint(p2.ident(2), true);
1445
+ try log.faint(.{ p2.ident(2), true });
1140
1446
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1141
- stream.reset();
1447
+ writer.reset();
1142
1448
  }
1143
1449
 
1144
1450
  test "|turn|" {
1145
- try log.turn(42);
1146
- var expected = switch (endian) {
1147
- .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1148
- .Little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
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) },
1149
1455
  };
1150
1456
  try expectLog1(expected, buf[0..4]);
1151
- stream.reset();
1457
+ writer.reset();
1152
1458
  }
1153
1459
 
1154
1460
  test "|win|" {
1155
- try log.win(.P2);
1461
+ try log.win(.{.P2});
1156
1462
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1157
- stream.reset();
1463
+ writer.reset();
1158
1464
  }
1159
1465
 
1160
1466
  test "|tie|" {
1161
- try log.tie();
1467
+ try log.tie(.{});
1162
1468
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1163
- stream.reset();
1469
+ writer.reset();
1164
1470
  }
1165
1471
 
1166
1472
  test "|-damage|" {
1167
1473
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1168
1474
  chansey.hp = 612;
1169
1475
  chansey.status = gen1.Status.slp(1);
1170
- try log.damage(p2.ident(2), &chansey, .None);
1476
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1171
1477
  var expected: []const u8 = switch (endian) {
1172
- .Big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1173
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
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) },
1174
1480
  };
1175
1481
  try expectLog1(expected, buf[0..8]);
1176
- stream.reset();
1482
+ writer.reset();
1177
1483
 
1178
1484
  chansey.hp = 100;
1179
1485
  chansey.stats.hp = 256;
1180
1486
  chansey.status = 0;
1181
- try log.damage(p2.ident(2), &chansey, .Confusion);
1487
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1182
1488
  expected = switch (endian) {
1183
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1184
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
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) },
1185
1491
  };
1186
1492
  try expectLog1(expected, buf[0..8]);
1187
- stream.reset();
1493
+ writer.reset();
1188
1494
 
1189
1495
  chansey.status = gen1.Status.init(.PSN);
1190
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1496
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1191
1497
  expected = switch (endian) {
1192
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1193
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
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 },
1194
1500
  };
1195
1501
  try expectLog1(expected, buf[0..9]);
1196
- stream.reset();
1502
+ writer.reset();
1197
1503
  }
1198
1504
 
1199
1505
  test "|-heal|" {
1200
1506
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1201
1507
  chansey.hp = 612;
1202
1508
  chansey.status = gen1.Status.slp(1);
1203
- try log.heal(p2.ident(2), &chansey, .None);
1509
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1204
1510
  var expected: []const u8 = switch (endian) {
1205
- .Big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1206
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
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) },
1207
1513
  };
1208
1514
  try expectLog1(expected, buf[0..8]);
1209
- stream.reset();
1515
+ writer.reset();
1210
1516
 
1211
1517
  chansey.hp = 100;
1212
1518
  chansey.stats.hp = 256;
1213
1519
  chansey.status = 0;
1214
- try log.heal(p2.ident(2), &chansey, .Silent);
1520
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1215
1521
  expected = switch (endian) {
1216
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1217
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
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) },
1218
1524
  };
1219
1525
  try expectLog1(expected, buf[0..8]);
1220
- stream.reset();
1526
+ writer.reset();
1221
1527
 
1222
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1528
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1223
1529
  expected = switch (endian) {
1224
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1225
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
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 },
1226
1532
  };
1227
1533
  try expectLog1(expected, buf[0..9]);
1228
- stream.reset();
1534
+ writer.reset();
1229
1535
  }
1230
1536
 
1231
1537
  test "|-status|" {
1232
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1538
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1233
1539
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1234
- stream.reset();
1540
+ writer.reset();
1235
1541
 
1236
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1542
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1237
1543
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1238
- stream.reset();
1544
+ writer.reset();
1239
1545
 
1240
- 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 });
1241
1547
  try expectLog1(
1242
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1548
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1243
1549
  buf[0..5],
1244
1550
  );
1245
- stream.reset();
1551
+ writer.reset();
1246
1552
  }
1247
1553
 
1248
1554
  test "|-curestatus|" {
1249
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1555
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1250
1556
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1251
- stream.reset();
1557
+ writer.reset();
1252
1558
 
1253
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1559
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1254
1560
  try expectLog1(&.{
1255
1561
  N(ArgType.CureStatus),
1256
1562
  0b0010,
1257
1563
  0b10001000,
1258
1564
  N(CureStatus.Silent),
1259
1565
  }, buf[0..4]);
1260
- stream.reset();
1566
+ writer.reset();
1261
1567
  }
1262
1568
 
1263
1569
  test "|-boost|" {
1264
- try log.boost(p2.ident(6), .Speed, 2);
1570
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1265
1571
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1266
- stream.reset();
1572
+ writer.reset();
1267
1573
 
1268
- try log.boost(p1.ident(2), .Rage, 1);
1574
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1269
1575
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1270
- stream.reset();
1576
+ writer.reset();
1271
1577
 
1272
- try log.boost(p2.ident(3), .Defense, -2);
1578
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1273
1579
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1274
- stream.reset();
1580
+ writer.reset();
1275
1581
  }
1276
1582
 
1277
1583
  test "|-clearallboost|" {
1278
- try log.clearallboost();
1584
+ try log.clearallboost(.{});
1279
1585
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1280
- stream.reset();
1586
+ writer.reset();
1281
1587
  }
1282
1588
 
1283
1589
  test "|-fail|" {
1284
- try log.fail(p2.ident(6), .None);
1590
+ try log.fail(.{ p2.ident(6), .None });
1285
1591
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1286
- stream.reset();
1592
+ writer.reset();
1287
1593
 
1288
- try log.fail(p2.ident(6), .Sleep);
1594
+ try log.fail(.{ p2.ident(6), .Sleep });
1289
1595
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1290
- stream.reset();
1596
+ writer.reset();
1291
1597
 
1292
- try log.fail(p2.ident(6), .Substitute);
1598
+ try log.fail(.{ p2.ident(6), .Substitute });
1293
1599
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1294
- stream.reset();
1600
+ writer.reset();
1295
1601
 
1296
- try log.fail(p2.ident(6), .Weak);
1602
+ try log.fail(.{ p2.ident(6), .Weak });
1297
1603
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1298
- stream.reset();
1604
+ writer.reset();
1299
1605
  }
1300
1606
 
1301
1607
  test "|-miss|" {
1302
- try log.miss(p2.ident(4));
1608
+ try log.miss(.{p2.ident(4)});
1303
1609
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1304
- stream.reset();
1610
+ writer.reset();
1305
1611
  }
1306
1612
  test "|-hitcount|" {
1307
- try log.hitcount(p2.ident(1), 5);
1613
+ try log.hitcount(.{ p2.ident(1), 5 });
1308
1614
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1309
- stream.reset();
1615
+ writer.reset();
1310
1616
  }
1311
1617
 
1312
1618
  test "|-prepare|" {
1313
- try log.prepare(p2.ident(2), M.Dig);
1314
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1315
- 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();
1316
1622
  }
1317
1623
 
1318
1624
  test "|-mustrecharge|" {
1319
- try log.mustrecharge(p1.ident(6));
1625
+ try log.mustrecharge(.{p1.ident(6)});
1320
1626
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1321
- stream.reset();
1627
+ writer.reset();
1322
1628
  }
1323
1629
 
1324
1630
  test "|-activate|" {
1325
- try log.activate(p1.ident(2), .Struggle);
1631
+ try log.activate(.{ p1.ident(2), .Struggle });
1326
1632
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1327
- stream.reset();
1633
+ writer.reset();
1328
1634
 
1329
- try log.activate(p2.ident(4), .Mist);
1635
+ try log.activate(.{ p2.ident(4), .Mist });
1330
1636
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1331
- stream.reset();
1637
+ writer.reset();
1332
1638
 
1333
- try log.activate(p2.ident(6), .Substitute);
1639
+ try log.activate(.{ p2.ident(6), .Substitute });
1334
1640
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1335
- stream.reset();
1641
+ writer.reset();
1336
1642
 
1337
- try log.activate(p1.ident(2), .Splash);
1643
+ try log.activate(.{ p1.ident(2), .Splash });
1338
1644
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1339
- 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();
1340
1659
  }
1341
1660
 
1342
1661
  test "|-fieldactivate|" {
1343
- try log.fieldactivate();
1662
+ try log.fieldactivate(.{});
1344
1663
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1345
- stream.reset();
1664
+ writer.reset();
1346
1665
  }
1347
1666
 
1348
1667
  test "|-start|" {
1349
- try log.start(p2.ident(6), .Bide);
1668
+ try log.start(.{ p2.ident(6), .Bide });
1350
1669
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1351
- stream.reset();
1670
+ writer.reset();
1352
1671
 
1353
- try log.start(p1.ident(2), .ConfusionSilent);
1672
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1354
1673
  try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1355
- stream.reset();
1674
+ writer.reset();
1356
1675
 
1357
- 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
+ });
1358
1682
  try expectLog1(
1359
1683
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1360
1684
  buf[0..5],
1361
1685
  );
1362
- stream.reset();
1686
+ writer.reset();
1363
1687
 
1364
- 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
+ });
1365
1694
  try expectLog1(
1366
1695
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1367
1696
  buf[0..5],
1368
1697
  );
1369
- stream.reset();
1698
+ writer.reset();
1370
1699
 
1371
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1372
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M.Surf) }, buf[0..4]);
1373
- 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();
1374
1703
 
1375
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1376
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M.Surf) }, buf[0..4]);
1377
- 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();
1378
1707
  }
1379
1708
 
1380
1709
  test "|-end|" {
1381
- try log.end(p2.ident(6), .Bide);
1710
+ try log.end(.{ p2.ident(6), .Bide });
1382
1711
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1383
- stream.reset();
1712
+ writer.reset();
1384
1713
 
1385
- try log.end(p1.ident(2), .ConfusionSilent);
1714
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1386
1715
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1387
- stream.reset();
1716
+ writer.reset();
1388
1717
  }
1389
1718
 
1390
1719
  test "|-ohko|" {
1391
- try log.ohko();
1720
+ try log.ohko(.{});
1392
1721
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1393
- stream.reset();
1722
+ writer.reset();
1394
1723
  }
1724
+
1395
1725
  test "|-crit|" {
1396
- try log.crit(p2.ident(5));
1726
+ try log.crit(.{p2.ident(5)});
1397
1727
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1398
- stream.reset();
1728
+ writer.reset();
1399
1729
  }
1730
+
1400
1731
  test "|-supereffective|" {
1401
- try log.supereffective(p1.ident(1));
1732
+ try log.supereffective(.{p1.ident(1)});
1402
1733
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1403
- stream.reset();
1734
+ writer.reset();
1404
1735
  }
1736
+
1405
1737
  test "|-resisted|" {
1406
- try log.resisted(p2.ident(2));
1738
+ try log.resisted(.{p2.ident(2)});
1407
1739
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1408
- stream.reset();
1740
+ writer.reset();
1409
1741
  }
1742
+
1410
1743
  test "|-immune|" {
1411
- try log.immune(p1.ident(3), .None);
1744
+ try log.immune(.{ p1.ident(3), .None });
1412
1745
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1413
- stream.reset();
1746
+ writer.reset();
1414
1747
 
1415
- try log.immune(p2.ident(2), .OHKO);
1748
+ try log.immune(.{ p2.ident(2), .OHKO });
1416
1749
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1417
- stream.reset();
1750
+ writer.reset();
1418
1751
  }
1752
+
1419
1753
  test "|-transform|" {
1420
- try log.transform(p2.ident(4), p1.ident(5));
1754
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1421
1755
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1422
- 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();
1423
1872
  }