@pkmn/engine 0.1.0-dev.fb5bf955 → 0.1.0-dev.fb6653d6

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