@pkmn/engine 0.1.0-dev.d97c6e03 → 0.1.0-dev.dc33f678

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 (51) hide show
  1. package/README.md +67 -56
  2. package/build/pkg/addon.d.ts +1 -1
  3. package/build/pkg/addon.js +7 -6
  4. package/build/pkg/addon.js.map +1 -1
  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 +2 -2
  9. package/build/pkg/gen1.d.ts +5 -4
  10. package/build/pkg/gen1.js +48 -32
  11. package/build/pkg/gen1.js.map +1 -1
  12. package/build/pkg/index.d.ts +15 -12
  13. package/build/pkg/index.js.map +1 -1
  14. package/build/pkg/protocol.d.ts +4 -2
  15. package/build/pkg/protocol.js +60 -39
  16. package/build/pkg/protocol.js.map +1 -1
  17. package/build/test/display.js +29 -12
  18. package/build/test/display.js.map +1 -1
  19. package/build.zig +92 -68
  20. package/package.json +40 -21
  21. package/src/bin/install-pkmn-engine +11 -6
  22. package/src/lib/binding/node.zig +46 -141
  23. package/src/lib/binding/wasm.zig +5 -3
  24. package/src/lib/common/battle.zig +17 -0
  25. package/src/lib/common/data.zig +15 -27
  26. package/src/lib/common/debug.zig +21 -11
  27. package/src/lib/common/js.zig +118 -0
  28. package/src/lib/common/optional.zig +67 -0
  29. package/src/lib/common/options.zig +12 -3
  30. package/src/lib/common/protocol.zig +771 -245
  31. package/src/lib/common/rational.zig +353 -0
  32. package/src/lib/common/rng.zig +15 -15
  33. package/src/lib/common/util.zig +12 -0
  34. package/src/lib/gen1/README.md +285 -182
  35. package/src/lib/gen1/calc.zig +426 -0
  36. package/src/lib/gen1/chance.zig +1060 -0
  37. package/src/lib/gen1/data/moves.zig +192 -180
  38. package/src/lib/gen1/data/species.zig +2 -2
  39. package/src/lib/gen1/data/types.zig +5 -5
  40. package/src/lib/gen1/data.zig +79 -65
  41. package/src/lib/gen1/helpers.zig +577 -32
  42. package/src/lib/gen1/mechanics.zig +940 -614
  43. package/src/lib/gen2/README.md +106 -85
  44. package/src/lib/gen2/data/items.zig +12 -18
  45. package/src/lib/gen2/data/moves.zig +660 -566
  46. package/src/lib/gen2/data/species.zig +1 -1
  47. package/src/lib/gen2/data/types.zig +131 -28
  48. package/src/lib/gen2/data.zig +132 -84
  49. package/src/lib/gen2/helpers.zig +16 -22
  50. package/src/lib/gen2/mechanics.zig +2426 -12
  51. package/src/lib/pkmn.zig +65 -6
@@ -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) {
@@ -177,6 +198,18 @@ pub const Activate = enum(u8) {
177
198
  Struggle,
178
199
  Substitute,
179
200
  Splash,
201
+
202
+ Rage, // TODO
203
+
204
+ Attract,
205
+ LockOn,
206
+ Bind,
207
+ Wrap,
208
+ FireSpin,
209
+ Clamp,
210
+ Whirlpool,
211
+
212
+ BeatUp, // FIXME of
180
213
  };
181
214
 
182
215
  pub const Start = enum(u8) {
@@ -194,6 +227,19 @@ pub const Start = enum(u8) {
194
227
 
195
228
  Disable,
196
229
  Mimic,
230
+
231
+ Attract,
232
+ Nightmare,
233
+ Curse,
234
+ Foresight,
235
+ Encore,
236
+ FutureSight,
237
+
238
+ PerishSong0,
239
+ PerishSong1,
240
+ PerishSong2,
241
+ PerishSong3,
242
+ PerishSong3Silent,
197
243
  };
198
244
 
199
245
  pub const End = enum(u8) {
@@ -202,16 +248,27 @@ pub const End = enum(u8) {
202
248
  Bide,
203
249
  Substitute,
204
250
 
205
- // Silent
251
+ Nightmare,
252
+ Curse,
253
+ Foresight,
254
+ Encore,
255
+ FutureSight,
256
+ LeechSeed,
257
+ Bind,
258
+ Wrap,
259
+ FireSpin,
260
+ Clamp,
261
+ Whirlpool,
262
+
206
263
  DisableSilent,
207
264
  ConfusionSilent,
208
-
209
- Mist,
210
- FocusEnergy,
211
- LeechSeed,
212
- Toxic,
213
- LightScreen,
214
- Reflect,
265
+ MistSilent,
266
+ FocusEnergySilent,
267
+ LeechSeedSilent,
268
+ ToxicSilent,
269
+ LightScreenSilent,
270
+ ReflectSilent,
271
+ BideSilent,
215
272
  };
216
273
 
217
274
  pub const Immune = enum(u8) {
@@ -219,8 +276,34 @@ pub const Immune = enum(u8) {
219
276
  OHKO,
220
277
  };
221
278
 
279
+ pub const EndItem = enum(u8) {
280
+ None,
281
+ Eat,
282
+ };
283
+
284
+ pub const SetHP = enum(u8) {
285
+ None,
286
+ Silent,
287
+ };
288
+
289
+ pub const Side = enum(u8) {
290
+ Safeguard,
291
+ Reflect,
292
+ LightScreen,
293
+ Spikes,
294
+ };
295
+
296
+ pub const Weather = enum(u8) {
297
+ None,
298
+ Upkeep,
299
+ };
300
+
301
+ /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
302
+ /// Ignores anything sent to it, though protocol logging should additionally be turned off
303
+ /// entirely with `options.log`.
222
304
  pub const NULL = Log(@TypeOf(std.io.null_writer)){ .writer = std.io.null_writer };
223
305
 
306
+ /// Logs protocol information to its `Writer` during a battle update when `options.log` is enabled.
224
307
  pub fn Log(comptime Writer: type) type {
225
308
  return struct {
226
309
  const Self = @This();
@@ -229,92 +312,118 @@ pub fn Log(comptime Writer: type) type {
229
312
 
230
313
  pub const Error = Writer.Error;
231
314
 
232
- pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
233
- if (!trace) return;
315
+ pub fn move(self: Self, source: ID, m: anytype, target: ID) Error!void {
316
+ if (!enabled) return;
317
+
234
318
  assert(m != .None);
235
319
  try self.writer.writeAll(&.{
236
- @enumToInt(ArgType.Move),
237
- @bitCast(u8, source),
238
- @enumToInt(m),
239
- @bitCast(u8, target),
320
+ @intFromEnum(ArgType.Move),
321
+ @as(u8, @bitCast(source)),
322
+ @intFromEnum(m),
323
+ @as(u8, @bitCast(target)),
324
+ @intFromEnum(Move.None),
240
325
  });
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
- },
326
+ }
327
+
328
+ pub fn moveFrom(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
329
+ if (!enabled) return;
330
+
331
+ assert(m != .None);
332
+ try self.writer.writeAll(&.{
333
+ @intFromEnum(ArgType.Move),
334
+ @as(u8, @bitCast(source)),
335
+ @intFromEnum(m),
336
+ @as(u8, @bitCast(target)),
252
337
  });
338
+ try self.writer.writeAll(if (@intFromEnum(from) == 0)
339
+ &.{@intFromEnum(Move.None)}
340
+ else
341
+ &.{ @intFromEnum(Move.From), @intFromEnum(from) });
253
342
  }
254
343
 
255
344
  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 });
345
+ return switchDrag(self, .Switch, ident, pokemon);
346
+ }
347
+
348
+ fn switchDrag(self: Self, arg: ArgType, ident: ID, pokemon: anytype) Error!void {
349
+ if (!enabled) return;
350
+
351
+ try self.writer.writeAll(&.{ @intFromEnum(arg), @as(u8, @bitCast(ident)) });
352
+ if (@hasField(@TypeOf(pokemon.*), "dvs")) {
353
+ try self.writer.writeAll(&.{
354
+ @intFromEnum(pokemon.species),
355
+ @intFromEnum(pokemon.dvs.gender),
356
+ pokemon.level,
357
+ });
358
+ } else {
359
+ try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
360
+ }
259
361
  try self.writer.writeIntNative(u16, pokemon.hp);
260
362
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
261
363
  try self.writer.writeAll(&.{pokemon.status});
262
364
  }
263
365
 
264
366
  pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
265
- if (!trace) return;
367
+ if (!enabled) return;
368
+
266
369
  assert(reason != .Disable);
267
370
  try self.writer.writeAll(&.{
268
- @enumToInt(ArgType.Cant),
269
- @bitCast(u8, ident),
270
- @enumToInt(reason),
371
+ @intFromEnum(ArgType.Cant),
372
+ @as(u8, @bitCast(ident)),
373
+ @intFromEnum(reason),
271
374
  });
272
375
  }
273
376
 
274
377
  pub fn disabled(self: Self, ident: ID, m: anytype) Error!void {
275
- if (!trace) return;
378
+ if (!enabled) return;
379
+
276
380
  try self.writer.writeAll(&.{
277
- @enumToInt(ArgType.Cant),
278
- @bitCast(u8, ident),
279
- @enumToInt(Cant.Disable),
280
- @enumToInt(m),
381
+ @intFromEnum(ArgType.Cant),
382
+ @as(u8, @bitCast(ident)),
383
+ @intFromEnum(Cant.Disable),
384
+ @intFromEnum(m),
281
385
  });
282
386
  }
283
387
 
284
388
  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));
389
+ if (!enabled) return;
390
+
391
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(ident)) });
392
+ if (done) try self.writer.writeByte(@intFromEnum(ArgType.None));
288
393
  }
289
394
 
290
395
  pub fn turn(self: Self, num: u16) Error!void {
291
- if (!trace) return;
292
- try self.writer.writeByte(@enumToInt(ArgType.Turn));
396
+ if (!enabled) return;
397
+
398
+ try self.writer.writeByte(@intFromEnum(ArgType.Turn));
293
399
  try self.writer.writeIntNative(u16, num);
294
- try self.writer.writeByte(@enumToInt(ArgType.None));
400
+ try self.writer.writeByte(@intFromEnum(ArgType.None));
295
401
  }
296
402
 
297
403
  pub fn win(self: Self, player: Player) Error!void {
298
- if (!trace) return;
404
+ if (!enabled) return;
405
+
299
406
  try self.writer.writeAll(&.{
300
- @enumToInt(ArgType.Win),
301
- @enumToInt(player),
302
- @enumToInt(ArgType.None),
407
+ @intFromEnum(ArgType.Win),
408
+ @intFromEnum(player),
409
+ @intFromEnum(ArgType.None),
303
410
  });
304
411
  }
305
412
 
306
413
  pub fn tie(self: Self) Error!void {
307
- if (!trace) return;
308
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Tie), @enumToInt(ArgType.None) });
414
+ if (!enabled) return;
415
+
416
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Tie), @intFromEnum(ArgType.None) });
309
417
  }
310
418
 
311
419
  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) });
420
+ if (!enabled) return;
421
+
422
+ assert(@intFromEnum(reason) <= @intFromEnum(Damage.LeechSeed));
423
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
315
424
  try self.writer.writeIntNative(u16, pokemon.hp);
316
425
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
317
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
426
+ try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
318
427
  }
319
428
 
320
429
  pub fn damageOf(
@@ -324,227 +433,398 @@ pub fn Log(comptime Writer: type) type {
324
433
  reason: Damage,
325
434
  source: ID,
326
435
  ) Error!void {
327
- if (!trace) return;
328
- assert(@enumToInt(reason) == @enumToInt(Damage.RecoilOf));
329
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
436
+ if (!enabled) return;
437
+
438
+ assert(@intFromEnum(reason) == @intFromEnum(Damage.RecoilOf));
439
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
330
440
  try self.writer.writeIntNative(u16, pokemon.hp);
331
441
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
332
442
  try self.writer.writeAll(&.{
333
443
  pokemon.status,
334
- @enumToInt(reason),
335
- @bitCast(u8, source),
444
+ @intFromEnum(reason),
445
+ @as(u8, @bitCast(source)),
336
446
  });
337
447
  }
338
448
 
339
449
  pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) Error!void {
340
- if (!trace) return;
450
+ if (!enabled) return;
451
+
341
452
  assert(reason != .Drain);
342
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, ident) });
453
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(ident)) });
343
454
  try self.writer.writeIntNative(u16, pokemon.hp);
344
455
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
345
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
456
+ try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
346
457
  }
347
458
 
348
459
  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) });
460
+ if (!enabled) return;
461
+
462
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(source)) });
351
463
  try self.writer.writeIntNative(u16, pokemon.hp);
352
464
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
353
465
  try self.writer.writeAll(&.{
354
466
  pokemon.status,
355
- @enumToInt(Heal.Drain),
356
- @bitCast(u8, target),
467
+ @intFromEnum(Heal.Drain),
468
+ @as(u8, @bitCast(target)),
357
469
  });
358
470
  }
359
471
 
360
472
  pub fn status(self: Self, ident: ID, value: u8, reason: Status) Error!void {
361
- if (!trace) return;
473
+ if (!enabled) return;
474
+
362
475
  assert(reason != .From);
363
476
  try self.writer.writeAll(&.{
364
- @enumToInt(ArgType.Status),
365
- @bitCast(u8, ident),
477
+ @intFromEnum(ArgType.Status),
478
+ @as(u8, @bitCast(ident)),
366
479
  value,
367
- @enumToInt(reason),
480
+ @intFromEnum(reason),
368
481
  });
369
482
  }
370
483
 
371
484
  pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
372
- if (!trace) return;
485
+ if (!enabled) return;
486
+
373
487
  try self.writer.writeAll(&.{
374
- @enumToInt(ArgType.Status),
375
- @bitCast(u8, ident),
488
+ @intFromEnum(ArgType.Status),
489
+ @as(u8, @bitCast(ident)),
376
490
  value,
377
- @enumToInt(Status.From),
378
- @enumToInt(m),
491
+ @intFromEnum(Status.From),
492
+ @intFromEnum(m),
379
493
  });
380
494
  }
381
495
 
382
496
  pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
383
- if (!trace) return;
497
+ if (!enabled) return;
498
+
384
499
  try self.writer.writeAll(&.{
385
- @enumToInt(ArgType.CureStatus),
386
- @bitCast(u8, ident),
500
+ @intFromEnum(ArgType.CureStatus),
501
+ @as(u8, @bitCast(ident)),
387
502
  value,
388
- @enumToInt(reason),
503
+ @intFromEnum(reason),
389
504
  });
390
505
  }
391
506
 
392
507
  pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
393
- if (!trace) return;
508
+ if (!enabled) return;
509
+
394
510
  assert(num != 0 and (reason != .Rage or num > 0));
395
511
  try self.writer.writeAll(&.{
396
- @enumToInt(ArgType.Boost),
397
- @bitCast(u8, ident),
398
- @enumToInt(reason),
399
- @intCast(u8, num + 6),
512
+ @intFromEnum(ArgType.Boost),
513
+ @as(u8, @bitCast(ident)),
514
+ @intFromEnum(reason),
515
+ @as(u8, @intCast(num + 6)),
400
516
  });
401
517
  }
402
518
 
403
519
  pub fn clearallboost(self: Self) Error!void {
404
- if (!trace) return;
405
- try self.writer.writeAll(&.{@enumToInt(ArgType.ClearAllBoost)});
520
+ if (!enabled) return;
521
+
522
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.ClearAllBoost)});
406
523
  }
407
524
 
408
525
  pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
409
- if (!trace) return;
526
+ if (!enabled) return;
527
+
410
528
  try self.writer.writeAll(&.{
411
- @enumToInt(ArgType.Fail),
412
- @bitCast(u8, ident),
413
- @enumToInt(reason),
529
+ @intFromEnum(ArgType.Fail),
530
+ @as(u8, @bitCast(ident)),
531
+ @intFromEnum(reason),
414
532
  });
415
533
  }
416
534
 
417
535
  pub fn miss(self: Self, source: ID) Error!void {
418
- if (!trace) return;
419
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Miss), @bitCast(u8, source) });
536
+ if (!enabled) return;
537
+
538
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(source)) });
420
539
  }
421
540
 
422
541
  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 });
542
+ if (!enabled) return;
543
+
544
+ try self.writer.writeAll(&.{
545
+ @intFromEnum(ArgType.HitCount),
546
+ @as(u8, @bitCast(ident)),
547
+ num,
548
+ });
425
549
  }
426
550
 
427
551
  pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
428
- if (!trace) return;
552
+ if (!enabled) return;
553
+
429
554
  try self.writer.writeAll(&.{
430
- @enumToInt(ArgType.Prepare),
431
- @bitCast(u8, source),
432
- @enumToInt(m),
555
+ @intFromEnum(ArgType.Prepare),
556
+ @as(u8, @bitCast(source)),
557
+ @intFromEnum(m),
433
558
  });
434
559
  }
435
560
 
436
561
  pub fn mustrecharge(self: Self, ident: ID) Error!void {
437
- if (!trace) return;
438
- try self.writer.writeAll(&.{ @enumToInt(ArgType.MustRecharge), @bitCast(u8, ident) });
562
+ if (!enabled) return;
563
+
564
+ try self.writer.writeAll(&.{
565
+ @intFromEnum(ArgType.MustRecharge),
566
+ @as(u8, @bitCast(ident)),
567
+ });
439
568
  }
440
569
 
441
570
  pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
442
- if (!trace) return;
571
+ if (!enabled) return;
572
+
443
573
  try self.writer.writeAll(&.{
444
- @enumToInt(ArgType.Activate),
445
- @bitCast(u8, ident),
446
- @enumToInt(reason),
574
+ @intFromEnum(ArgType.Activate),
575
+ @as(u8, @bitCast(ident)),
576
+ @intFromEnum(reason),
447
577
  });
448
578
  }
449
579
 
450
580
  pub fn fieldactivate(self: Self) Error!void {
451
- if (!trace) return;
452
- try self.writer.writeAll(&.{@enumToInt(ArgType.FieldActivate)});
581
+ if (!enabled) return;
582
+
583
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.FieldActivate)});
453
584
  }
454
585
 
455
586
  pub fn start(self: Self, ident: ID, reason: Start) Error!void {
456
- if (!trace) return;
457
- assert(@enumToInt(reason) < @enumToInt(Start.TypeChange));
587
+ if (!enabled) return;
588
+
589
+ assert(@intFromEnum(reason) < @intFromEnum(Start.TypeChange));
458
590
  try self.writer.writeAll(&.{
459
- @enumToInt(ArgType.Start),
460
- @bitCast(u8, ident),
461
- @enumToInt(reason),
591
+ @intFromEnum(ArgType.Start),
592
+ @as(u8, @bitCast(ident)),
593
+ @intFromEnum(reason),
462
594
  });
463
595
  }
464
596
 
465
597
  pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
466
- if (!trace) return;
598
+ if (!enabled) return;
599
+
467
600
  try self.writer.writeAll(&.{
468
- @enumToInt(ArgType.Start),
469
- @bitCast(u8, source),
470
- @enumToInt(Start.TypeChange),
471
- @bitCast(u8, types),
472
- @bitCast(u8, target),
601
+ @intFromEnum(ArgType.Start),
602
+ @as(u8, @bitCast(source)),
603
+ @intFromEnum(Start.TypeChange),
604
+ @as(u8, @bitCast(types)),
605
+ @as(u8, @bitCast(target)),
473
606
  });
474
607
  }
475
608
 
476
609
  pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) Error!void {
477
- if (!trace) return;
478
- assert(@enumToInt(reason) > @enumToInt(Start.TypeChange));
610
+ if (!enabled) return;
611
+
612
+ assert(@intFromEnum(reason) > @intFromEnum(Start.TypeChange));
479
613
  try self.writer.writeAll(&.{
480
- @enumToInt(ArgType.Start),
481
- @bitCast(u8, ident),
482
- @enumToInt(reason),
483
- @enumToInt(m),
614
+ @intFromEnum(ArgType.Start),
615
+ @as(u8, @bitCast(ident)),
616
+ @intFromEnum(reason),
617
+ @intFromEnum(m),
484
618
  });
485
619
  }
486
620
 
487
621
  pub fn end(self: Self, ident: ID, reason: End) Error!void {
488
- if (!trace) return;
622
+ if (!enabled) return;
623
+
489
624
  try self.writer.writeAll(&.{
490
- @enumToInt(ArgType.End),
491
- @bitCast(u8, ident),
492
- @enumToInt(reason),
625
+ @intFromEnum(ArgType.End),
626
+ @as(u8, @bitCast(ident)),
627
+ @intFromEnum(reason),
493
628
  });
494
629
  }
495
630
 
496
631
  pub fn ohko(self: Self) Error!void {
497
- if (!trace) return;
498
- try self.writer.writeAll(&.{@enumToInt(ArgType.OHKO)});
632
+ if (!enabled) return;
633
+
634
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
499
635
  }
500
636
 
501
637
  pub fn crit(self: Self, ident: ID) Error!void {
502
- if (!trace) return;
503
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Crit), @bitCast(u8, ident) });
638
+ if (!enabled) return;
639
+
640
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(ident)) });
504
641
  }
505
642
 
506
643
  pub fn supereffective(self: Self, ident: ID) Error!void {
507
- if (!trace) return;
508
- try self.writer.writeAll(&.{ @enumToInt(ArgType.SuperEffective), @bitCast(u8, ident) });
644
+ if (!enabled) return;
645
+
646
+ try self.writer.writeAll(&.{
647
+ @intFromEnum(ArgType.SuperEffective),
648
+ @as(u8, @bitCast(ident)),
649
+ });
509
650
  }
510
651
 
511
652
  pub fn resisted(self: Self, ident: ID) Error!void {
512
- if (!trace) return;
513
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Resisted), @bitCast(u8, ident) });
653
+ if (!enabled) return;
654
+
655
+ try self.writer.writeAll(&.{
656
+ @intFromEnum(ArgType.Resisted),
657
+ @as(u8, @bitCast(ident)),
658
+ });
514
659
  }
515
660
 
516
661
  pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
517
- if (!trace) return;
662
+ if (!enabled) return;
663
+
518
664
  try self.writer.writeAll(&.{
519
- @enumToInt(ArgType.Immune),
520
- @bitCast(u8, ident),
521
- @enumToInt(reason),
665
+ @intFromEnum(ArgType.Immune),
666
+ @as(u8, @bitCast(ident)),
667
+ @intFromEnum(reason),
522
668
  });
523
669
  }
524
670
 
525
671
  pub fn transform(self: Self, source: ID, target: ID) Error!void {
526
- if (!trace) return;
672
+ if (!enabled) return;
673
+
674
+ try self.writer.writeAll(&.{
675
+ @intFromEnum(ArgType.Transform),
676
+ @as(u8, @bitCast(source)),
677
+ @as(u8, @bitCast(target)),
678
+ });
679
+ }
680
+
681
+ pub fn drag(self: Self, ident: ID, pokemon: anytype) Error!void {
682
+ return switchDrag(self, .Drag, ident, pokemon);
683
+ }
684
+
685
+ pub fn item(self: Self, target: ID, i: anytype, source: ID) Error!void {
686
+ if (!enabled) return;
687
+
688
+ try self.writer.writeAll(&.{
689
+ @intFromEnum(ArgType.Item),
690
+ @as(u8, @bitCast(target)),
691
+ @intFromEnum(i),
692
+ @as(u8, @bitCast(source)),
693
+ });
694
+ }
695
+
696
+ pub fn enditem(self: Self, ident: ID, i: anytype, reason: EndItem) Error!void {
697
+ if (!enabled) return;
698
+
527
699
  try self.writer.writeAll(&.{
528
- @enumToInt(ArgType.Transform),
529
- @bitCast(u8, source),
530
- @bitCast(u8, target),
700
+ @intFromEnum(ArgType.EndItem),
701
+ @as(u8, @bitCast(ident)),
702
+ @intFromEnum(i),
703
+ @intFromEnum(reason),
704
+ });
705
+ }
706
+
707
+ pub fn cureteam(self: Self, source: ID) Error!void {
708
+ if (!enabled) return;
709
+
710
+ try self.writer.writeAll(&.{
711
+ @intFromEnum(ArgType.CureTeam),
712
+ @as(u8, @bitCast(source)),
713
+ });
714
+ }
715
+
716
+ pub fn sethp(self: Self, ident: ID, pokemon: anytype, reason: SetHP) Error!void {
717
+ if (!enabled) return;
718
+
719
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(ident)) });
720
+ try self.writer.writeIntNative(u16, pokemon.hp);
721
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
722
+ try self.writer.writeAll(&.{pokemon.status});
723
+ try self.writer.writeByte(@intFromEnum(reason));
724
+ }
725
+
726
+ pub fn setboost(self: Self, ident: ID, num: i8) Error!void {
727
+ if (!enabled) return;
728
+
729
+ try self.writer.writeAll(&.{
730
+ @intFromEnum(ArgType.SetBoost),
731
+ @as(u8, @bitCast(ident)),
732
+ @as(u8, @intCast(num + 6)),
733
+ });
734
+ }
735
+
736
+ pub fn copyboost(self: Self, source: ID, target: ID) Error!void {
737
+ if (!enabled) return;
738
+
739
+ try self.writer.writeAll(&.{
740
+ @intFromEnum(ArgType.CopyBoost),
741
+ @as(u8, @bitCast(source)),
742
+ @as(u8, @bitCast(target)),
743
+ });
744
+ }
745
+
746
+ pub fn sidestart(self: Self, player: Player, reason: Side) Error!void {
747
+ if (!enabled) return;
748
+
749
+ try self.writer.writeAll(&.{
750
+ @intFromEnum(ArgType.SideStart),
751
+ @intFromEnum(player),
752
+ @intFromEnum(reason),
753
+ });
754
+ }
755
+
756
+ pub fn sideend(self: Self, player: Player, reason: Side) Error!void {
757
+ if (!enabled) return;
758
+
759
+ assert(@intFromEnum(reason) != @intFromEnum(Side.Spikes));
760
+ try self.writer.writeAll(&.{
761
+ @intFromEnum(ArgType.SideEnd),
762
+ @intFromEnum(player),
763
+ @intFromEnum(reason),
764
+ });
765
+ }
766
+
767
+ pub fn spikesend(self: Self, player: Player, m: anytype, source: ID) Error!void {
768
+ if (!enabled) return;
769
+
770
+ try self.writer.writeAll(&.{
771
+ @intFromEnum(ArgType.SideEnd),
772
+ @intFromEnum(player),
773
+ @intFromEnum(Side.Spikes),
774
+ @intFromEnum(m),
775
+ @as(u8, @bitCast(source)),
776
+ });
777
+ }
778
+
779
+ pub fn singlemove(self: Self, ident: ID, m: anytype) Error!void {
780
+ return single(self, .SingleMove, ident, m);
781
+ }
782
+
783
+ pub fn singleturn(self: Self, ident: ID, m: anytype) Error!void {
784
+ return single(self, .SingleTurn, ident, m);
785
+ }
786
+
787
+ fn single(self: Self, arg: ArgType, ident: ID, m: anytype) Error!void {
788
+ if (!enabled) return;
789
+
790
+ try self.writer.writeAll(&.{
791
+ @intFromEnum(arg),
792
+ @as(u8, @bitCast(ident)),
793
+ @intFromEnum(m),
794
+ });
795
+ }
796
+
797
+ pub fn weather(self: Self, w: anytype, reason: Weather) Error!void {
798
+ if (!enabled) return;
799
+
800
+ assert(!(w == .None and reason == .Upkeep));
801
+ try self.writer.writeAll(&.{
802
+ @intFromEnum(ArgType.Weather),
803
+ @intFromEnum(w),
804
+ @intFromEnum(reason),
531
805
  });
532
806
  }
533
807
 
534
808
  pub fn laststill(self: Self) Error!void {
535
- if (!trace) return;
536
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastStill)});
809
+ if (!enabled) return;
810
+
811
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
537
812
  }
538
813
 
539
814
  pub fn lastmiss(self: Self) Error!void {
540
- if (!trace) return;
541
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastMiss)});
815
+ if (!enabled) return;
816
+
817
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastMiss)});
542
818
  }
543
819
  };
544
820
  }
545
821
 
822
+ /// `Log` type backed by the optimized `ByteStream.Writer`.
546
823
  pub const FixedLog = Log(ByteStream.Writer);
547
824
 
825
+ /// Stripped down version of `std.io.FixedBufferStream` optimized for efficiently writing the
826
+ /// individual protocol bytes. Note that the `ByteStream.Writer` is **not** a `std.io.Writer` and
827
+ /// should not be used for general purpose writing.
548
828
  pub const ByteStream = struct {
549
829
  buffer: []u8,
550
830
  pos: usize = 0,
@@ -585,15 +865,19 @@ pub const ByteStream = struct {
585
865
  }
586
866
  };
587
867
 
588
- pub const Kind = enum { Move, Species, Type, Status };
589
- pub const Formatter = fn (Kind, u8) []const u8;
868
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
590
869
 
591
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
870
+ pub fn format(
871
+ comptime gen: anytype,
872
+ a: []const u8,
873
+ b: ?[]const u8,
874
+ color: bool,
875
+ ) void {
592
876
  print("\n", .{});
593
877
 
594
878
  var i: usize = 0;
595
879
  while (i < a.len) {
596
- const arg = @intToEnum(ArgType, a[i]);
880
+ const arg: ArgType = @enumFromInt(a[i]);
597
881
  const name = switch (arg) {
598
882
  .None => if (color) "\x1b[2m-\x1b[0m" else "-",
599
883
  .LastStill => "|[still]",
@@ -626,6 +910,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
626
910
  .Resisted => "|-resisted|",
627
911
  .Immune => "|-immune|",
628
912
  .Transform => "|-transform|",
913
+ .Drag => "|drag|",
914
+ .Item => "|-item|",
915
+ .EndItem => "|-enditem|",
916
+ .CureTeam => "|-cureteam|",
917
+ .SetHP => "|-sethp|",
918
+ .SetBoost => "|-setboost|",
919
+ .CopyBoost => "|-copyboost|",
920
+ .SideStart => "|-sidestart|",
921
+ .SideEnd => "|-sideend|",
922
+ .SingleMove => "|-singlemove|",
923
+ .SingleTurn => "|-singleturn|",
924
+ .Weather => "|-weather|",
629
925
  else => unreachable,
630
926
  };
631
927
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -639,19 +935,29 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
639
935
  .OHKO,
640
936
  => {},
641
937
  .Move => {
642
- const source = ID.from(@intCast(u4, a[i]));
938
+ const source = ID.from(@intCast(a[i]));
643
939
  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]));
940
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
941
+ const target = ID.from(@intCast(a[i]));
646
942
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
647
- const reason = @intToEnum(Move, a[i]);
943
+ const reason: Move = @enumFromInt(a[i]);
648
944
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
649
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
945
+ if (reason == .From) {
946
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
947
+ }
650
948
  },
651
- .Switch => {
652
- const id = ID.from(@intCast(u4, a[i]));
949
+ .Switch, .Drag => {
950
+ const id = ID.from(@intCast(a[i]));
653
951
  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);
952
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
953
+ if (@hasDecl(gen, "Gender")) {
954
+ if (a[i] == N(gen.Gender.Unknown)) {
955
+ i += 1;
956
+ } else {
957
+ var gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
958
+ printc(", {s}", .{gender}, a, b, &i, 1, color);
959
+ }
960
+ }
655
961
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
656
962
  switch (endian) {
657
963
  .Big => {
@@ -667,15 +973,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
667
973
  printc("/{d}", .{hp}, a, b, &i, 2, color);
668
974
  },
669
975
  }
670
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
976
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
671
977
  },
672
978
  .Cant => {
673
- const id = ID.from(@intCast(u4, a[i]));
979
+ const id = ID.from(@intCast(a[i]));
674
980
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
675
- const reason = @intToEnum(Cant, a[i]);
981
+ const reason: Cant = @enumFromInt(a[i]);
676
982
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
677
983
  if (reason == .Disable) {
678
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
984
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
679
985
  }
680
986
  },
681
987
  .Faint,
@@ -684,8 +990,9 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
684
990
  .Crit,
685
991
  .SuperEffective,
686
992
  .Resisted,
993
+ .CureTeam,
687
994
  => {
688
- const id = ID.from(@intCast(u4, a[i]));
995
+ const id = ID.from(@intCast(a[i]));
689
996
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
690
997
  },
691
998
  .Turn => {
@@ -695,9 +1002,11 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
695
1002
  };
696
1003
  printc(" {d}", .{turn}, a, b, &i, 2, color);
697
1004
  },
698
- .Win => printc(" {s}", .{@tagName(@intToEnum(Player, a[i]))}, a, b, &i, 1, color),
1005
+ .Win => {
1006
+ printc(" {s}", .{@tagName(@as(Player, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1007
+ },
699
1008
  .Damage, .Heal => {
700
- var id = ID.from(@intCast(u4, a[i]));
1009
+ var id = ID.from(@intCast(a[i]));
701
1010
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
702
1011
  switch (endian) {
703
1012
  .Big => {
@@ -713,95 +1022,150 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
713
1022
  printc("/{d}", .{hp}, a, b, &i, 2, color);
714
1023
  },
715
1024
  }
716
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1025
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
717
1026
  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]));
1027
+ const reason: Damage = @enumFromInt(a[i]);
1028
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1029
+ if (reason == .RecoilOf) {
1030
+ id = ID.from(@intCast(a[i]));
722
1031
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
723
1032
  }
724
1033
  } else {
725
- const reason = @intToEnum(Heal, a[i]);
1034
+ const reason: Heal = @enumFromInt(a[i]);
726
1035
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
727
1036
  if (reason == .Drain) {
728
- id = ID.from(@intCast(u4, a[i]));
1037
+ id = ID.from(@intCast(a[i]));
729
1038
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
730
1039
  }
731
1040
  }
732
1041
  },
733
1042
  .Status => {
734
- const id = ID.from(@intCast(u4, a[i]));
1043
+ const id = ID.from(@intCast(a[i]));
735
1044
  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]);
1045
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1046
+ const reason: Status = @enumFromInt(a[i]);
738
1047
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
739
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1048
+ if (reason == .From) {
1049
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1050
+ }
740
1051
  },
741
1052
  .CureStatus => {
742
- const id = ID.from(@intCast(u4, a[i]));
1053
+ const id = ID.from(@intCast(a[i]));
743
1054
  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);
1055
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1056
+ const reason: CureStatus = @enumFromInt(a[i]);
1057
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
746
1058
  },
747
1059
  .Boost => {
748
- const id = ID.from(@intCast(u4, a[i]));
1060
+ const id = ID.from(@intCast(a[i]));
749
1061
  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);
1062
+ printc(" {s}", .{@tagName(@as(Boost, @enumFromInt(a[i])))}, a, b, &i, 1, color);
751
1063
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
752
1064
  },
753
1065
  .Fail => {
754
- const id = ID.from(@intCast(u4, a[i]));
1066
+ const id = ID.from(@intCast(a[i]));
755
1067
  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);
1068
+ printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
757
1069
  },
758
- .HitCount => {
759
- const id = ID.from(@intCast(u4, a[i]));
1070
+ .HitCount, .SetBoost => {
1071
+ const id = ID.from(@intCast(a[i]));
760
1072
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
761
1073
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
762
1074
  },
763
- .Prepare => {
764
- const id = ID.from(@intCast(u4, a[i]));
1075
+ .Prepare, .SingleMove, .SingleTurn => {
1076
+ const id = ID.from(@intCast(a[i]));
765
1077
  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);
1078
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
767
1079
  },
768
1080
  .Activate => {
769
- const id = ID.from(@intCast(u4, a[i]));
1081
+ const id = ID.from(@intCast(a[i]));
770
1082
  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);
1083
+ printc(" {s}", .{@tagName(@as(Activate, @enumFromInt(a[i])))}, a, b, &i, 1, color);
772
1084
  },
773
1085
  .Start => {
774
- const id = ID.from(@intCast(u4, a[i]));
1086
+ const id = ID.from(@intCast(a[i]));
775
1087
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
776
1088
  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]);
1089
+ printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
1090
+ if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
1091
+ const types = @as(gen1.Types, @bitCast(a[i]));
780
1092
  const args = .{
781
- formatter(.Type, @enumToInt(types.type1)),
782
- formatter(.Type, @enumToInt(types.type2)),
1093
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1094
+ formatter(gen, .Type, @intFromEnum(types.type2)),
783
1095
  };
784
1096
  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);
1097
+ } else if (reason >= @intFromEnum(Start.Disable)) {
1098
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
787
1099
  }
788
1100
  },
789
1101
  .End => {
790
- const id = ID.from(@intCast(u4, a[i]));
1102
+ const id = ID.from(@intCast(a[i]));
791
1103
  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);
1104
+ printc(" {s}", .{@tagName(@as(End, @enumFromInt(a[i])))}, a, b, &i, 1, color);
793
1105
  },
794
1106
  .Immune => {
795
- const id = ID.from(@intCast(u4, a[i]));
1107
+ const id = ID.from(@intCast(a[i]));
796
1108
  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);
1109
+ printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
798
1110
  },
799
- .Transform => {
800
- const source = ID.from(@intCast(u4, a[i]));
1111
+ .Transform, .CopyBoost => {
1112
+ const source = ID.from(@intCast(a[i]));
801
1113
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
802
- const target = ID.from(@intCast(u4, a[i]));
1114
+ const target = ID.from(@intCast(a[i]));
803
1115
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
804
1116
  },
1117
+ .Item, .EndItem => {
1118
+ var id = ID.from(@intCast(a[i]));
1119
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1120
+ printc(" {s}", .{formatter(gen, .Item, a[i])}, a, b, &i, 1, color);
1121
+ if (arg == .Item) {
1122
+ id = ID.from(@intCast(a[i]));
1123
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1124
+ } else {
1125
+ const reason: EndItem = @enumFromInt(a[i]);
1126
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1127
+ }
1128
+ },
1129
+ .SetHP => {
1130
+ const id = ID.from(@intCast(a[i]));
1131
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1132
+ switch (endian) {
1133
+ .Big => {
1134
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1135
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1136
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1137
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1138
+ },
1139
+ .Little => {
1140
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1141
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1142
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1143
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1144
+ },
1145
+ }
1146
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1147
+ printc(" {s}", .{@tagName(@as(SetHP, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1148
+ },
1149
+ .SideStart => {
1150
+ const id = ID.from(@intCast(a[i]));
1151
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1152
+ printc(" {s}", .{@tagName(@as(Side, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1153
+ },
1154
+ .SideEnd => {
1155
+ var id = ID.from(@intCast(a[i]));
1156
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1157
+ const reason: Side = @enumFromInt(a[i]);
1158
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1159
+ if (reason == .Spikes) {
1160
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1161
+ id = ID.from(@intCast(a[i]));
1162
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1163
+ }
1164
+ },
1165
+ .Weather => {
1166
+ printc(" {s}", .{formatter(gen, .Weather, a[i])}, a, b, &i, 1, color);
1167
+ printc(" {s}", .{@tagName(@as(Weather, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1168
+ },
805
1169
  else => unreachable,
806
1170
  }
807
1171
  print("\n", .{});
@@ -833,12 +1197,13 @@ fn printc(
833
1197
  }
834
1198
 
835
1199
  pub fn expectLog(
836
- comptime formatter: Formatter,
1200
+ comptime gen: anytype,
837
1201
  expected: []const u8,
838
1202
  actual: []const u8,
839
1203
  offset: usize,
840
1204
  ) !void {
841
- if (!trace) return;
1205
+ if (!enabled) return;
1206
+
842
1207
  const color = color: {
843
1208
  if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
844
1209
  break :color true;
@@ -851,8 +1216,8 @@ pub fn expectLog(
851
1216
 
852
1217
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
853
1218
  error.TestExpectedEqual => {
854
- format(formatter, expected, null, color);
855
- format(formatter, actual, expected, color);
1219
+ format(gen, expected, null, color);
1220
+ format(gen, actual, expected, color);
856
1221
  return err;
857
1222
  },
858
1223
  else => return err,
@@ -881,7 +1246,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
881
1246
  const endian = builtin.target.cpu.arch.endian();
882
1247
 
883
1248
  fn N(e: anytype) u8 {
884
- return @enumToInt(e);
1249
+ return @intFromEnum(e);
885
1250
  }
886
1251
 
887
1252
  const p1 = Player.P1;
@@ -892,49 +1257,70 @@ const gen1 = struct {
892
1257
  pub const helpers = @import("../gen1/helpers.zig");
893
1258
  };
894
1259
 
1260
+ const gen2 = struct {
1261
+ pub usingnamespace @import("../gen2/data.zig");
1262
+ pub const helpers = @import("../gen2/helpers.zig");
1263
+ };
1264
+
895
1265
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
896
- var stream = ByteStream{ .buffer = &buf };
1266
+ var stream: ByteStream = .{ .buffer = &buf };
897
1267
  var log: FixedLog = .{ .writer = stream.writer() };
898
1268
 
899
- const M = gen1.Move;
900
- const S = gen1.Species;
1269
+ const M1 = gen1.Move;
1270
+ const S1 = gen1.Species;
1271
+
1272
+ const M2 = gen2.Move;
1273
+ const S2 = gen2.Species;
1274
+ const I2 = gen2.Item;
1275
+ const W2 = gen2.Weather;
901
1276
 
902
1277
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
903
- return expectLog(gen1Formatter, expected, actual, 0);
1278
+ return expectLog(gen1, expected, actual, 0);
904
1279
  }
905
1280
 
906
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1281
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1282
+ return expectLog(gen2, expected, actual, 0);
1283
+ }
1284
+
1285
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
907
1286
  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),
1287
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1288
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1289
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1290
+ .Status => gen.Status.name(byte),
1291
+ .Item => if (@hasDecl(gen, "Item"))
1292
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1293
+ else
1294
+ unreachable,
1295
+ .Weather => if (@hasDecl(gen, "Weather"))
1296
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1297
+ else
1298
+ unreachable,
912
1299
  };
913
1300
  }
914
1301
 
915
1302
  test "|move|" {
916
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1303
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5));
917
1304
  try expectLog1(
918
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1305
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
919
1306
  buf[0..5],
920
1307
  );
921
1308
  stream.reset();
922
1309
 
923
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
924
- const wrap = N(M.Wrap);
1310
+ try log.moveFrom(p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome);
925
1311
  try expectLog1(
926
- &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
1312
+ &.{ N(ArgType.Move), 0b1100, N(M1.Pound), 0b0101, N(Move.From), N(M1.Metronome) },
927
1313
  buf[0..6],
928
1314
  );
929
1315
  stream.reset();
930
1316
 
931
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1317
+ try log.move(p2.ident(4), M1.SkullBash, .{});
932
1318
  try log.laststill();
933
1319
  try expectLog1(
934
1320
  &.{
935
1321
  N(ArgType.Move),
936
1322
  0b1100,
937
- N(M.SkullBash),
1323
+ N(M1.SkullBash),
938
1324
  0,
939
1325
  N(Move.None),
940
1326
  N(ArgType.LastStill),
@@ -943,13 +1329,13 @@ test "|move|" {
943
1329
  );
944
1330
  stream.reset();
945
1331
 
946
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1332
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5));
947
1333
  try log.lastmiss();
948
1334
  try expectLog1(
949
1335
  &.{
950
1336
  N(ArgType.Move),
951
1337
  0b1100,
952
- N(M.Tackle),
1338
+ N(M1.Tackle),
953
1339
  0b0101,
954
1340
  N(Move.None),
955
1341
  N(ArgType.LastMiss),
@@ -968,8 +1354,8 @@ test "|switch|" {
968
1354
  try log.switched(p2.ident(3), &snorlax);
969
1355
  const par = 0b1000000;
970
1356
  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 },
1357
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1358
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
973
1359
  };
974
1360
  try expectLog1(expected, buf[0..9]);
975
1361
  stream.reset();
@@ -979,8 +1365,8 @@ test "|switch|" {
979
1365
  snorlax.status = 0;
980
1366
  try log.switched(p2.ident(3), &snorlax);
981
1367
  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 },
1368
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1369
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
984
1370
  };
985
1371
  try expectLog1(expected, buf[0..9]);
986
1372
  stream.reset();
@@ -988,11 +1374,30 @@ test "|switch|" {
988
1374
  snorlax.hp = 400;
989
1375
  try log.switched(p2.ident(3), &snorlax);
990
1376
  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 },
1377
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1378
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
993
1379
  };
994
1380
  try expectLog1(expected, buf[0..9]);
995
1381
  stream.reset();
1382
+
1383
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1384
+ blissey.level = 91;
1385
+ blissey.hp = 200;
1386
+ blissey.stats.hp = 400;
1387
+ blissey.status = gen2.Status.init(.PAR);
1388
+ try log.switched(p2.ident(3), &blissey);
1389
+ expected = &(.{
1390
+ N(ArgType.Switch),
1391
+ 0b1011,
1392
+ N(S2.Blissey),
1393
+ N(gen2.Gender.Female),
1394
+ 91,
1395
+ } ++ switch (endian) {
1396
+ .Big => .{ 0, 200, 1, 144, par },
1397
+ .Little => .{ 200, 0, 144, 1, par },
1398
+ });
1399
+ try expectLog2(expected, buf[0..10]);
1400
+ stream.reset();
996
1401
  }
997
1402
 
998
1403
  test "|cant|" {
@@ -1000,8 +1405,8 @@ test "|cant|" {
1000
1405
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1001
1406
  stream.reset();
1002
1407
 
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]);
1408
+ try log.disabled(p1.ident(2), M1.Earthquake);
1409
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1005
1410
  stream.reset();
1006
1411
  }
1007
1412
 
@@ -1111,9 +1516,9 @@ test "|-status|" {
1111
1516
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1112
1517
  stream.reset();
1113
1518
 
1114
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1519
+ try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M1.BodySlam);
1115
1520
  try expectLog1(
1116
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1521
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1117
1522
  buf[0..5],
1118
1523
  );
1119
1524
  stream.reset();
@@ -1184,8 +1589,8 @@ test "|-hitcount|" {
1184
1589
  }
1185
1590
 
1186
1591
  test "|-prepare|" {
1187
- try log.prepare(p2.ident(2), M.Dig);
1188
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1592
+ try log.prepare(p2.ident(2), M1.Dig);
1593
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1189
1594
  stream.reset();
1190
1595
  }
1191
1596
 
@@ -1242,12 +1647,12 @@ test "|-start|" {
1242
1647
  );
1243
1648
  stream.reset();
1244
1649
 
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]);
1650
+ try log.startEffect(p1.ident(2), .Disable, M1.Surf);
1651
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1247
1652
  stream.reset();
1248
1653
 
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]);
1654
+ try log.startEffect(p1.ident(2), .Mimic, M1.Surf);
1655
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1251
1656
  stream.reset();
1252
1657
  }
1253
1658
 
@@ -1266,21 +1671,25 @@ test "|-ohko|" {
1266
1671
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1267
1672
  stream.reset();
1268
1673
  }
1674
+
1269
1675
  test "|-crit|" {
1270
1676
  try log.crit(p2.ident(5));
1271
1677
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1272
1678
  stream.reset();
1273
1679
  }
1680
+
1274
1681
  test "|-supereffective|" {
1275
1682
  try log.supereffective(p1.ident(1));
1276
1683
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1277
1684
  stream.reset();
1278
1685
  }
1686
+
1279
1687
  test "|-resisted|" {
1280
1688
  try log.resisted(p2.ident(2));
1281
1689
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1282
1690
  stream.reset();
1283
1691
  }
1692
+
1284
1693
  test "|-immune|" {
1285
1694
  try log.immune(p1.ident(3), .None);
1286
1695
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1290,8 +1699,125 @@ test "|-immune|" {
1290
1699
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1291
1700
  stream.reset();
1292
1701
  }
1702
+
1293
1703
  test "|-transform|" {
1294
1704
  try log.transform(p2.ident(4), p1.ident(5));
1295
1705
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1296
1706
  stream.reset();
1297
1707
  }
1708
+
1709
+ test "|drag|" {
1710
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1711
+ blissey.level = 91;
1712
+ blissey.hp = 200;
1713
+ blissey.stats.hp = 400;
1714
+ blissey.status = gen2.Status.init(.PAR);
1715
+ try log.drag(p2.ident(3), &blissey);
1716
+ const par = 0b1000000;
1717
+ const expected = &(.{
1718
+ N(ArgType.Drag),
1719
+ 0b1011,
1720
+ N(S2.Blissey),
1721
+ N(gen2.Gender.Female),
1722
+ 91,
1723
+ } ++ switch (endian) {
1724
+ .Big => .{ 0, 200, 1, 144, par },
1725
+ .Little => .{ 200, 0, 144, 1, par },
1726
+ });
1727
+ try expectLog2(expected, buf[0..10]);
1728
+ stream.reset();
1729
+ }
1730
+
1731
+ test "|-item|" {
1732
+ try log.item(p2.ident(2), I2.GoldBerry, p1.ident(4));
1733
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1734
+ stream.reset();
1735
+ }
1736
+
1737
+ test "|-enditem|" {
1738
+ try log.enditem(p1.ident(1), I2.BerserkGene, .None);
1739
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1740
+ stream.reset();
1741
+
1742
+ try log.enditem(p2.ident(4), I2.PRZCureBerry, .Eat);
1743
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1744
+ stream.reset();
1745
+ }
1746
+
1747
+ test "|-cureteam|" {
1748
+ try log.cureteam(p2.ident(5));
1749
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1750
+ stream.reset();
1751
+ }
1752
+
1753
+ test "|-sethp|" {
1754
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1755
+ blissey.hp = 612;
1756
+ try log.sethp(p2.ident(2), &blissey, .Silent);
1757
+ var expected: []const u8 = switch (endian) {
1758
+ .Big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1759
+ .Little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1760
+ };
1761
+ try expectLog2(expected, buf[0..8]);
1762
+ stream.reset();
1763
+ }
1764
+
1765
+ test "|-setboost|" {
1766
+ try log.setboost(p2.ident(6), 6);
1767
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1768
+ stream.reset();
1769
+
1770
+ try log.setboost(p2.ident(3), 2);
1771
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1772
+ stream.reset();
1773
+ }
1774
+
1775
+ test "|-copyboost|" {
1776
+ try log.copyboost(p2.ident(3), p1.ident(6));
1777
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1778
+ stream.reset();
1779
+ }
1780
+
1781
+ test "|-sidestart|" {
1782
+ try log.sidestart(.P2, .Reflect);
1783
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1784
+ stream.reset();
1785
+ }
1786
+
1787
+ test "|-sideend|" {
1788
+ try log.sideend(.P2, .LightScreen);
1789
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1790
+ stream.reset();
1791
+
1792
+ try log.spikesend(.P1, M2.RapidSpin, p1.ident(3));
1793
+ try expectLog2(&.{
1794
+ N(ArgType.SideEnd),
1795
+ 0,
1796
+ N(Side.Spikes),
1797
+ N(M2.RapidSpin),
1798
+ 0b0011,
1799
+ }, buf[0..5]);
1800
+ stream.reset();
1801
+ }
1802
+
1803
+ test "|-singlemove|" {
1804
+ try log.singlemove(p2.ident(2), M2.DestinyBond);
1805
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1806
+ stream.reset();
1807
+ }
1808
+
1809
+ test "|-singleturn|" {
1810
+ try log.singleturn(p1.ident(3), M2.Protect);
1811
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1812
+ stream.reset();
1813
+ }
1814
+
1815
+ test "|-weather|" {
1816
+ try log.weather(W2.Rain, .Upkeep);
1817
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1818
+ stream.reset();
1819
+
1820
+ try log.weather(W2.None, .None);
1821
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1822
+ stream.reset();
1823
+ }