@pkmn/engine 0.1.0-dev.2ec67dba → 0.1.0-dev.30c63015

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