@pkmn/engine 0.1.0-dev.545e0ef3 → 0.1.0-dev.5bbdaae3

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