@pkmn/engine 0.1.0-dev.a324c50f → 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 (41) hide show
  1. package/README.md +45 -37
  2. package/build/pkg/data/ids.json +12 -12
  3. package/build/pkg/data/layout.json +2 -2
  4. package/build/pkg/data/protocol.json +69 -12
  5. package/build/pkg/data.d.ts +2 -2
  6. package/build/pkg/gen1.js +2 -2
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/protocol.js +56 -37
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build.zig +53 -44
  11. package/package.json +40 -19
  12. package/src/bin/install-pkmn-engine +2 -2
  13. package/src/lib/binding/node.zig +1 -1
  14. package/src/lib/common/battle.zig +17 -0
  15. package/src/lib/common/data.zig +4 -16
  16. package/src/lib/common/js.zig +118 -0
  17. package/src/lib/common/optional.zig +67 -0
  18. package/src/lib/common/protocol.zig +561 -80
  19. package/src/lib/common/rational.zig +353 -0
  20. package/src/lib/common/rng.zig +6 -6
  21. package/src/lib/common/util.zig +12 -0
  22. package/src/lib/gen1/README.md +273 -193
  23. package/src/lib/gen1/calc.zig +426 -0
  24. package/src/lib/gen1/chance.zig +1060 -0
  25. package/src/lib/gen1/data/moves.zig +183 -171
  26. package/src/lib/gen1/data/species.zig +1 -1
  27. package/src/lib/gen1/data/types.zig +3 -3
  28. package/src/lib/gen1/data.zig +31 -38
  29. package/src/lib/gen1/helpers.zig +241 -103
  30. package/src/lib/gen1/mechanics.zig +111 -166
  31. package/src/lib/gen2/README.md +106 -85
  32. package/src/lib/gen2/calc.zig +143 -0
  33. package/src/lib/gen2/chance.zig +1314 -0
  34. package/src/lib/gen2/data/items.zig +11 -17
  35. package/src/lib/gen2/data/moves.zig +662 -568
  36. package/src/lib/gen2/data/species.zig +1 -1
  37. package/src/lib/gen2/data/types.zig +129 -26
  38. package/src/lib/gen2/data.zig +89 -64
  39. package/src/lib/gen2/generated.zig +857 -0
  40. package/src/lib/gen2/helpers.zig +8 -12
  41. package/src/lib/gen2/mechanics.zig +2994 -9
@@ -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,6 +279,28 @@ 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
+
222
304
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
223
305
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
224
306
  /// entirely with `options.log`.
@@ -233,7 +315,7 @@ pub fn Log(comptime Writer: type) type {
233
315
 
234
316
  pub const Error = Writer.Error;
235
317
 
236
- pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
318
+ pub fn move(self: Self, source: ID, m: anytype, target: ID) Error!void {
237
319
  if (!enabled) return;
238
320
 
239
321
  assert(m != .None);
@@ -242,26 +324,43 @@ pub fn Log(comptime Writer: type) type {
242
324
  @as(u8, @bitCast(source)),
243
325
  @intFromEnum(m),
244
326
  @as(u8, @bitCast(target)),
327
+ @intFromEnum(Move.None),
245
328
  });
246
- const none = &[_]u8{@intFromEnum(Move.None)};
247
- try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
248
- .Null => none,
249
- .Optional => if (from) |f| blk: {
250
- assert(@intFromEnum(f) != 0);
251
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
252
- } else none,
253
- else => blk: {
254
- assert(@intFromEnum(from) != 0);
255
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(from) };
256
- },
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)),
257
340
  });
341
+ try self.writer.writeAll(if (@intFromEnum(from) == 0)
342
+ &.{@intFromEnum(Move.None)}
343
+ else
344
+ &.{ @intFromEnum(Move.From), @intFromEnum(from) });
258
345
  }
259
346
 
260
347
  pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
348
+ return switchDrag(self, .Switch, ident, pokemon);
349
+ }
350
+
351
+ fn switchDrag(self: Self, arg: ArgType, ident: ID, pokemon: anytype) Error!void {
261
352
  if (!enabled) return;
262
353
 
263
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
264
- try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
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
+ }
265
364
  try self.writer.writeIntNative(u16, pokemon.hp);
266
365
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
267
366
  try self.writer.writeAll(&.{pokemon.status});
@@ -474,6 +573,7 @@ pub fn Log(comptime Writer: type) type {
474
573
  pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
475
574
  if (!enabled) return;
476
575
 
576
+ assert(reason != .SubstituteBlock); // TODO
477
577
  try self.writer.writeAll(&.{
478
578
  @intFromEnum(ArgType.Activate),
479
579
  @as(u8, @bitCast(ident)),
@@ -481,6 +581,18 @@ pub fn Log(comptime Writer: type) type {
481
581
  });
482
582
  }
483
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
588
+ try self.writer.writeAll(&.{
589
+ @intFromEnum(ArgType.Activate),
590
+ @as(u8, @bitCast(ident)),
591
+ @intFromEnum(reason),
592
+ @intFromEnum(m),
593
+ });
594
+ }
595
+
484
596
  pub fn fieldactivate(self: Self) Error!void {
485
597
  if (!enabled) return;
486
598
 
@@ -582,6 +694,133 @@ pub fn Log(comptime Writer: type) type {
582
694
  });
583
695
  }
584
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
+
765
+ try self.writer.writeAll(&.{
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),
821
+ });
822
+ }
823
+
585
824
  pub fn laststill(self: Self) Error!void {
586
825
  if (!enabled) return;
587
826
 
@@ -642,10 +881,14 @@ pub const ByteStream = struct {
642
881
  }
643
882
  };
644
883
 
645
- pub const Kind = enum { Move, Species, Type, Status };
646
- pub const Formatter = fn (Kind, u8) []const u8;
884
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
647
885
 
648
- 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 {
649
892
  print("\n", .{});
650
893
 
651
894
  var i: usize = 0;
@@ -683,6 +926,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
683
926
  .Resisted => "|-resisted|",
684
927
  .Immune => "|-immune|",
685
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|",
686
941
  else => unreachable,
687
942
  };
688
943
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -698,17 +953,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
698
953
  .Move => {
699
954
  const source = ID.from(@intCast(a[i]));
700
955
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
701
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
956
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
702
957
  const target = ID.from(@intCast(a[i]));
703
958
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
704
959
  const reason: Move = @enumFromInt(a[i]);
705
960
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
706
- 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
+ }
707
964
  },
708
- .Switch => {
965
+ .Switch, .Drag => {
709
966
  const id = ID.from(@intCast(a[i]));
710
967
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
711
- 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
+ }
712
977
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
713
978
  switch (endian) {
714
979
  .Big => {
@@ -724,7 +989,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
724
989
  printc("/{d}", .{hp}, a, b, &i, 2, color);
725
990
  },
726
991
  }
727
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
992
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
728
993
  },
729
994
  .Cant => {
730
995
  const id = ID.from(@intCast(a[i]));
@@ -732,7 +997,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
732
997
  const reason: Cant = @enumFromInt(a[i]);
733
998
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
734
999
  if (reason == .Disable) {
735
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1000
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
736
1001
  }
737
1002
  },
738
1003
  .Faint,
@@ -741,6 +1006,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
741
1006
  .Crit,
742
1007
  .SuperEffective,
743
1008
  .Resisted,
1009
+ .CureTeam,
744
1010
  => {
745
1011
  const id = ID.from(@intCast(a[i]));
746
1012
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
@@ -772,7 +1038,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
772
1038
  printc("/{d}", .{hp}, a, b, &i, 2, color);
773
1039
  },
774
1040
  }
775
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1041
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
776
1042
  if (arg == .Damage) {
777
1043
  const reason: Damage = @enumFromInt(a[i]);
778
1044
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -792,15 +1058,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
792
1058
  .Status => {
793
1059
  const id = ID.from(@intCast(a[i]));
794
1060
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
795
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1061
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
796
1062
  const reason: Status = @enumFromInt(a[i]);
797
1063
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
798
- 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
+ }
799
1067
  },
800
1068
  .CureStatus => {
801
1069
  const id = ID.from(@intCast(a[i]));
802
1070
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
803
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1071
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
804
1072
  const reason: CureStatus = @enumFromInt(a[i]);
805
1073
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
806
1074
  },
@@ -815,15 +1083,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
815
1083
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
816
1084
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
817
1085
  },
818
- .HitCount => {
1086
+ .HitCount, .SetBoost => {
819
1087
  const id = ID.from(@intCast(a[i]));
820
1088
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
821
1089
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
822
1090
  },
823
- .Prepare => {
1091
+ .Prepare, .SingleMove, .SingleTurn => {
824
1092
  const id = ID.from(@intCast(a[i]));
825
1093
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
826
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1094
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
827
1095
  },
828
1096
  .Activate => {
829
1097
  const id = ID.from(@intCast(a[i]));
@@ -838,12 +1106,12 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
838
1106
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
839
1107
  const types = @as(gen1.Types, @bitCast(a[i]));
840
1108
  const args = .{
841
- formatter(.Type, @intFromEnum(types.type1)),
842
- formatter(.Type, @intFromEnum(types.type2)),
1109
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1110
+ formatter(gen, .Type, @intFromEnum(types.type2)),
843
1111
  };
844
1112
  printc(" {s}/{s}", args, a, b, &i, 1, color);
845
1113
  } else if (reason >= @intFromEnum(Start.Disable)) {
846
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1114
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
847
1115
  }
848
1116
  },
849
1117
  .End => {
@@ -856,12 +1124,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
856
1124
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
857
1125
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
858
1126
  },
859
- .Transform => {
1127
+ .Transform, .CopyBoost => {
860
1128
  const source = ID.from(@intCast(a[i]));
861
1129
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
862
1130
  const target = ID.from(@intCast(a[i]));
863
1131
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
864
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
+ },
865
1185
  else => unreachable,
866
1186
  }
867
1187
  print("\n", .{});
@@ -893,7 +1213,7 @@ fn printc(
893
1213
  }
894
1214
 
895
1215
  pub fn expectLog(
896
- comptime formatter: Formatter,
1216
+ comptime gen: anytype,
897
1217
  expected: []const u8,
898
1218
  actual: []const u8,
899
1219
  offset: usize,
@@ -912,8 +1232,8 @@ pub fn expectLog(
912
1232
 
913
1233
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
914
1234
  error.TestExpectedEqual => {
915
- format(formatter, expected, null, color);
916
- format(formatter, actual, expected, color);
1235
+ format(gen, expected, null, color);
1236
+ format(gen, actual, expected, color);
917
1237
  return err;
918
1238
  },
919
1239
  else => return err,
@@ -953,49 +1273,70 @@ const gen1 = struct {
953
1273
  pub const helpers = @import("../gen1/helpers.zig");
954
1274
  };
955
1275
 
1276
+ const gen2 = struct {
1277
+ pub usingnamespace @import("../gen2/data.zig");
1278
+ pub const helpers = @import("../gen2/helpers.zig");
1279
+ };
1280
+
956
1281
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
957
- var stream = ByteStream{ .buffer = &buf };
1282
+ var stream: ByteStream = .{ .buffer = &buf };
958
1283
  var log: FixedLog = .{ .writer = stream.writer() };
959
1284
 
960
- const M = gen1.Move;
961
- 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;
962
1292
 
963
1293
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
964
- 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);
965
1299
  }
966
1300
 
967
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1301
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
968
1302
  return switch (kind) {
969
- .Move => @tagName(@as(M, @enumFromInt(byte))),
970
- .Species => @tagName(@as(S, @enumFromInt(byte))),
971
- .Type => @tagName(@as(gen1.Type, @enumFromInt(byte))),
972
- .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,
973
1315
  };
974
1316
  }
975
1317
 
976
1318
  test "|move|" {
977
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1319
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5));
978
1320
  try expectLog1(
979
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1321
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
980
1322
  buf[0..5],
981
1323
  );
982
1324
  stream.reset();
983
1325
 
984
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
985
- const wrap = N(M.Wrap);
1326
+ try log.moveFrom(p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome);
986
1327
  try expectLog1(
987
- &.{ 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) },
988
1329
  buf[0..6],
989
1330
  );
990
1331
  stream.reset();
991
1332
 
992
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1333
+ try log.move(p2.ident(4), M1.SkullBash, .{});
993
1334
  try log.laststill();
994
1335
  try expectLog1(
995
1336
  &.{
996
1337
  N(ArgType.Move),
997
1338
  0b1100,
998
- N(M.SkullBash),
1339
+ N(M1.SkullBash),
999
1340
  0,
1000
1341
  N(Move.None),
1001
1342
  N(ArgType.LastStill),
@@ -1004,13 +1345,13 @@ test "|move|" {
1004
1345
  );
1005
1346
  stream.reset();
1006
1347
 
1007
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1348
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5));
1008
1349
  try log.lastmiss();
1009
1350
  try expectLog1(
1010
1351
  &.{
1011
1352
  N(ArgType.Move),
1012
1353
  0b1100,
1013
- N(M.Tackle),
1354
+ N(M1.Tackle),
1014
1355
  0b0101,
1015
1356
  N(Move.None),
1016
1357
  N(ArgType.LastMiss),
@@ -1029,8 +1370,8 @@ test "|switch|" {
1029
1370
  try log.switched(p2.ident(3), &snorlax);
1030
1371
  const par = 0b1000000;
1031
1372
  var expected: []const u8 = switch (endian) {
1032
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
1033
- .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 },
1034
1375
  };
1035
1376
  try expectLog1(expected, buf[0..9]);
1036
1377
  stream.reset();
@@ -1040,8 +1381,8 @@ test "|switch|" {
1040
1381
  snorlax.status = 0;
1041
1382
  try log.switched(p2.ident(3), &snorlax);
1042
1383
  expected = switch (endian) {
1043
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
1044
- .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 },
1045
1386
  };
1046
1387
  try expectLog1(expected, buf[0..9]);
1047
1388
  stream.reset();
@@ -1049,11 +1390,30 @@ test "|switch|" {
1049
1390
  snorlax.hp = 400;
1050
1391
  try log.switched(p2.ident(3), &snorlax);
1051
1392
  expected = switch (endian) {
1052
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
1053
- .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 },
1054
1395
  };
1055
1396
  try expectLog1(expected, buf[0..9]);
1056
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();
1057
1417
  }
1058
1418
 
1059
1419
  test "|cant|" {
@@ -1061,8 +1421,8 @@ test "|cant|" {
1061
1421
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1062
1422
  stream.reset();
1063
1423
 
1064
- try log.disabled(p1.ident(2), M.Earthquake);
1065
- 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]);
1066
1426
  stream.reset();
1067
1427
  }
1068
1428
 
@@ -1172,9 +1532,9 @@ test "|-status|" {
1172
1532
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1173
1533
  stream.reset();
1174
1534
 
1175
- 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);
1176
1536
  try expectLog1(
1177
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1537
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1178
1538
  buf[0..5],
1179
1539
  );
1180
1540
  stream.reset();
@@ -1245,8 +1605,8 @@ test "|-hitcount|" {
1245
1605
  }
1246
1606
 
1247
1607
  test "|-prepare|" {
1248
- try log.prepare(p2.ident(2), M.Dig);
1249
- 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]);
1250
1610
  stream.reset();
1251
1611
  }
1252
1612
 
@@ -1303,12 +1663,12 @@ test "|-start|" {
1303
1663
  );
1304
1664
  stream.reset();
1305
1665
 
1306
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1307
- 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]);
1308
1668
  stream.reset();
1309
1669
 
1310
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1311
- 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]);
1312
1672
  stream.reset();
1313
1673
  }
1314
1674
 
@@ -1327,21 +1687,25 @@ test "|-ohko|" {
1327
1687
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1328
1688
  stream.reset();
1329
1689
  }
1690
+
1330
1691
  test "|-crit|" {
1331
1692
  try log.crit(p2.ident(5));
1332
1693
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1333
1694
  stream.reset();
1334
1695
  }
1696
+
1335
1697
  test "|-supereffective|" {
1336
1698
  try log.supereffective(p1.ident(1));
1337
1699
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1338
1700
  stream.reset();
1339
1701
  }
1702
+
1340
1703
  test "|-resisted|" {
1341
1704
  try log.resisted(p2.ident(2));
1342
1705
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1343
1706
  stream.reset();
1344
1707
  }
1708
+
1345
1709
  test "|-immune|" {
1346
1710
  try log.immune(p1.ident(3), .None);
1347
1711
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1351,8 +1715,125 @@ test "|-immune|" {
1351
1715
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1352
1716
  stream.reset();
1353
1717
  }
1718
+
1354
1719
  test "|-transform|" {
1355
1720
  try log.transform(p2.ident(4), p1.ident(5));
1356
1721
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1357
1722
  stream.reset();
1358
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
+ }