@pkmn/engine 0.1.0-dev.a5a9f4b2 → 0.1.0-dev.a93847b2

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