@pkmn/engine 0.1.0-dev.b9fb6eab → 0.1.0-dev.c42deb42

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