@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d73cd76c

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