@pkmn/engine 0.1.0-dev.0eb29f53 → 0.1.0-dev.1336408

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