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

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