@pkmn/engine 0.1.0-dev.cf6f5d2c → 0.1.0-dev.d56bdadd

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