@pkmn/engine 0.1.0-dev.47feedd9 → 0.1.0-dev.4cc7c5c2

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