@pkmn/engine 0.1.0-dev.21a51fe4 → 0.1.0-dev.21e728d9

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 (40) hide show
  1. package/README.md +11 -3
  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/tools/debug.js +3 -1
  11. package/build/tools/debug.js.map +1 -1
  12. package/build.zig +72 -57
  13. package/package.json +27 -15
  14. package/src/bin/install-pkmn-engine +1 -1
  15. package/src/lib/binding/node.zig +1 -1
  16. package/src/lib/common/data.zig +8 -20
  17. package/src/lib/common/optional.zig +8 -1
  18. package/src/lib/common/protocol.zig +578 -80
  19. package/src/lib/common/rational.zig +5 -5
  20. package/src/lib/common/rng.zig +15 -6
  21. package/src/lib/gen1/README.md +114 -32
  22. package/src/lib/gen1/calc.zig +4 -5
  23. package/src/lib/gen1/chance.zig +58 -42
  24. package/src/lib/gen1/data/moves.zig +192 -180
  25. package/src/lib/gen1/data/species.zig +2 -2
  26. package/src/lib/gen1/data/types.zig +7 -7
  27. package/src/lib/gen1/data.zig +31 -43
  28. package/src/lib/gen1/helpers.zig +254 -116
  29. package/src/lib/gen1/mechanics.zig +142 -176
  30. package/src/lib/gen2/README.md +93 -72
  31. package/src/lib/gen2/calc.zig +143 -0
  32. package/src/lib/gen2/chance.zig +1314 -0
  33. package/src/lib/gen2/data/items.zig +12 -18
  34. package/src/lib/gen2/data/moves.zig +663 -569
  35. package/src/lib/gen2/data/species.zig +1 -1
  36. package/src/lib/gen2/data/types.zig +134 -31
  37. package/src/lib/gen2/data.zig +100 -73
  38. package/src/lib/gen2/generated.zig +808 -0
  39. package/src/lib/gen2/helpers.zig +8 -12
  40. package/src/lib/gen2/mechanics.zig +3486 -9
@@ -121,12 +121,19 @@ pub const Cant = enum(u8) {
121
121
  Disable,
122
122
  Recharge,
123
123
  PP,
124
+
125
+ Attract,
124
126
  };
125
127
 
126
128
  pub const Heal = enum(u8) {
127
129
  None,
128
130
  Silent,
129
131
  Drain,
132
+
133
+ Leftovers,
134
+ Berry,
135
+ BerryJuice,
136
+ GoldBerry,
130
137
  };
131
138
 
132
139
  pub const Damage = enum(u8) {
@@ -136,6 +143,17 @@ pub const Damage = enum(u8) {
136
143
  Confusion,
137
144
  LeechSeed,
138
145
  RecoilOf,
146
+
147
+ Bind,
148
+ Wrap,
149
+ FireSpin,
150
+ Clamp,
151
+ Whirlpool,
152
+ Spikes,
153
+
154
+ Nightmare,
155
+ Curse,
156
+ Sandstorm,
139
157
  };
140
158
 
141
159
  pub const Status = enum(u8) {
@@ -178,8 +196,25 @@ pub const Activate = enum(u8) {
178
196
  Haze,
179
197
  Mist,
180
198
  Struggle,
181
- Substitute,
199
+ Substitute, // FIXME SubstituteDamage
182
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,
183
218
  };
184
219
 
185
220
  pub const Start = enum(u8) {
@@ -197,6 +232,19 @@ pub const Start = enum(u8) {
197
232
 
198
233
  Disable,
199
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,
200
248
  };
201
249
 
202
250
  pub const End = enum(u8) {
@@ -205,16 +253,30 @@ pub const End = enum(u8) {
205
253
  Bide,
206
254
  Substitute,
207
255
 
208
- // 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
+
209
269
  DisableSilent,
210
270
  ConfusionSilent,
211
-
212
- Mist,
213
- FocusEnergy,
214
- LeechSeed,
215
- Toxic,
216
- LightScreen,
217
- Reflect,
271
+ MistSilent,
272
+ FocusEnergySilent,
273
+ LeechSeedSilent,
274
+ ToxicSilent,
275
+ LightScreenSilent,
276
+ ReflectSilent,
277
+ BideSilent,
278
+
279
+ LeechSeedFrom,
218
280
  };
219
281
 
220
282
  pub const Immune = enum(u8) {
@@ -222,6 +284,28 @@ pub const Immune = enum(u8) {
222
284
  OHKO,
223
285
  };
224
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
+
225
309
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
226
310
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
227
311
  /// entirely with `options.log`.
@@ -236,7 +320,7 @@ pub fn Log(comptime Writer: type) type {
236
320
 
237
321
  pub const Error = Writer.Error;
238
322
 
239
- 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 {
240
324
  if (!enabled) return;
241
325
 
242
326
  assert(m != .None);
@@ -245,26 +329,43 @@ pub fn Log(comptime Writer: type) type {
245
329
  @as(u8, @bitCast(source)),
246
330
  @intFromEnum(m),
247
331
  @as(u8, @bitCast(target)),
332
+ @intFromEnum(Move.None),
248
333
  });
249
- const none = &[_]u8{@intFromEnum(Move.None)};
250
- try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
251
- .Null => none,
252
- .Optional => if (from) |f| blk: {
253
- assert(@intFromEnum(f) != 0);
254
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
255
- } else none,
256
- else => blk: {
257
- assert(@intFromEnum(from) != 0);
258
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(from) };
259
- },
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)),
260
345
  });
346
+ try self.writer.writeAll(if (@intFromEnum(from) == 0)
347
+ &.{@intFromEnum(Move.None)}
348
+ else
349
+ &.{ @intFromEnum(Move.From), @intFromEnum(from) });
261
350
  }
262
351
 
263
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 {
264
357
  if (!enabled) return;
265
358
 
266
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
267
- try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
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
+ }
268
369
  try self.writer.writeIntNative(u16, pokemon.hp);
269
370
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
270
371
  try self.writer.writeAll(&.{pokemon.status});
@@ -477,10 +578,34 @@ pub fn Log(comptime Writer: type) type {
477
578
  pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
478
579
  if (!enabled) return;
479
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
480
593
  try self.writer.writeAll(&.{
481
594
  @intFromEnum(ArgType.Activate),
482
595
  @as(u8, @bitCast(ident)),
483
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
+
604
+ try self.writer.writeAll(&.{
605
+ @intFromEnum(ArgType.Activate),
606
+ @as(u8, @bitCast(ident)),
607
+ @intFromEnum(Activate.Magnitude),
608
+ num,
484
609
  });
485
610
  }
486
611
 
@@ -585,6 +710,132 @@ pub fn Log(comptime Writer: type) type {
585
710
  });
586
711
  }
587
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));
792
+ try self.writer.writeAll(&.{
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),
836
+ });
837
+ }
838
+
588
839
  pub fn laststill(self: Self) Error!void {
589
840
  if (!enabled) return;
590
841
 
@@ -645,10 +896,14 @@ pub const ByteStream = struct {
645
896
  }
646
897
  };
647
898
 
648
- pub const Kind = enum { Move, Species, Type, Status };
649
- pub const Formatter = fn (Kind, u8) []const u8;
899
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
650
900
 
651
- 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 {
652
907
  print("\n", .{});
653
908
 
654
909
  var i: usize = 0;
@@ -686,6 +941,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
686
941
  .Resisted => "|-resisted|",
687
942
  .Immune => "|-immune|",
688
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|",
689
956
  else => unreachable,
690
957
  };
691
958
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -701,17 +968,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
701
968
  .Move => {
702
969
  const source = ID.from(@intCast(a[i]));
703
970
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
704
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
971
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
705
972
  const target = ID.from(@intCast(a[i]));
706
973
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
707
974
  const reason: Move = @enumFromInt(a[i]);
708
975
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
709
- 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
+ }
710
979
  },
711
- .Switch => {
980
+ .Switch, .Drag => {
712
981
  const id = ID.from(@intCast(a[i]));
713
982
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
714
- 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
+ }
715
992
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
716
993
  switch (endian) {
717
994
  .Big => {
@@ -727,7 +1004,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
727
1004
  printc("/{d}", .{hp}, a, b, &i, 2, color);
728
1005
  },
729
1006
  }
730
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1007
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
731
1008
  },
732
1009
  .Cant => {
733
1010
  const id = ID.from(@intCast(a[i]));
@@ -735,7 +1012,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
735
1012
  const reason: Cant = @enumFromInt(a[i]);
736
1013
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
737
1014
  if (reason == .Disable) {
738
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1015
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
739
1016
  }
740
1017
  },
741
1018
  .Faint,
@@ -744,6 +1021,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
744
1021
  .Crit,
745
1022
  .SuperEffective,
746
1023
  .Resisted,
1024
+ .CureTeam,
747
1025
  => {
748
1026
  const id = ID.from(@intCast(a[i]));
749
1027
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
@@ -775,7 +1053,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
775
1053
  printc("/{d}", .{hp}, a, b, &i, 2, color);
776
1054
  },
777
1055
  }
778
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1056
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
779
1057
  if (arg == .Damage) {
780
1058
  const reason: Damage = @enumFromInt(a[i]);
781
1059
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -795,15 +1073,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
795
1073
  .Status => {
796
1074
  const id = ID.from(@intCast(a[i]));
797
1075
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
798
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1076
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
799
1077
  const reason: Status = @enumFromInt(a[i]);
800
1078
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
801
- 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
+ }
802
1082
  },
803
1083
  .CureStatus => {
804
1084
  const id = ID.from(@intCast(a[i]));
805
1085
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
806
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1086
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
807
1087
  const reason: CureStatus = @enumFromInt(a[i]);
808
1088
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
809
1089
  },
@@ -818,15 +1098,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
818
1098
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
819
1099
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
820
1100
  },
821
- .HitCount => {
1101
+ .HitCount, .SetBoost => {
822
1102
  const id = ID.from(@intCast(a[i]));
823
1103
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
824
1104
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
825
1105
  },
826
- .Prepare => {
1106
+ .Prepare, .SingleMove, .SingleTurn => {
827
1107
  const id = ID.from(@intCast(a[i]));
828
1108
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
829
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1109
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
830
1110
  },
831
1111
  .Activate => {
832
1112
  const id = ID.from(@intCast(a[i]));
@@ -841,12 +1121,12 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
841
1121
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
842
1122
  const types = @as(gen1.Types, @bitCast(a[i]));
843
1123
  const args = .{
844
- formatter(.Type, @intFromEnum(types.type1)),
845
- formatter(.Type, @intFromEnum(types.type2)),
1124
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1125
+ formatter(gen, .Type, @intFromEnum(types.type2)),
846
1126
  };
847
1127
  printc(" {s}/{s}", args, a, b, &i, 1, color);
848
1128
  } else if (reason >= @intFromEnum(Start.Disable)) {
849
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1129
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
850
1130
  }
851
1131
  },
852
1132
  .End => {
@@ -859,12 +1139,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
859
1139
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
860
1140
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
861
1141
  },
862
- .Transform => {
1142
+ .Transform, .CopyBoost => {
863
1143
  const source = ID.from(@intCast(a[i]));
864
1144
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
865
1145
  const target = ID.from(@intCast(a[i]));
866
1146
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
867
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
+ },
868
1200
  else => unreachable,
869
1201
  }
870
1202
  print("\n", .{});
@@ -896,7 +1228,7 @@ fn printc(
896
1228
  }
897
1229
 
898
1230
  pub fn expectLog(
899
- comptime formatter: Formatter,
1231
+ comptime gen: anytype,
900
1232
  expected: []const u8,
901
1233
  actual: []const u8,
902
1234
  offset: usize,
@@ -915,8 +1247,8 @@ pub fn expectLog(
915
1247
 
916
1248
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
917
1249
  error.TestExpectedEqual => {
918
- format(formatter, expected, null, color);
919
- format(formatter, actual, expected, color);
1250
+ format(gen, expected, null, color);
1251
+ format(gen, actual, expected, color);
920
1252
  return err;
921
1253
  },
922
1254
  else => return err,
@@ -956,49 +1288,70 @@ const gen1 = struct {
956
1288
  pub const helpers = @import("../gen1/helpers.zig");
957
1289
  };
958
1290
 
1291
+ const gen2 = struct {
1292
+ pub usingnamespace @import("../gen2/data.zig");
1293
+ pub const helpers = @import("../gen2/helpers.zig");
1294
+ };
1295
+
959
1296
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
960
- var stream = ByteStream{ .buffer = &buf };
1297
+ var stream: ByteStream = .{ .buffer = &buf };
961
1298
  var log: FixedLog = .{ .writer = stream.writer() };
962
1299
 
963
- const M = gen1.Move;
964
- 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;
965
1307
 
966
1308
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
967
- return expectLog(gen1Formatter, expected, actual, 0);
1309
+ return expectLog(gen1, expected, actual, 0);
968
1310
  }
969
1311
 
970
- 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 {
971
1317
  return switch (kind) {
972
- .Move => @tagName(@as(M, @enumFromInt(byte))),
973
- .Species => @tagName(@as(S, @enumFromInt(byte))),
974
- .Type => @tagName(@as(gen1.Type, @enumFromInt(byte))),
975
- .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,
976
1330
  };
977
1331
  }
978
1332
 
979
1333
  test "|move|" {
980
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1334
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5));
981
1335
  try expectLog1(
982
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1336
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
983
1337
  buf[0..5],
984
1338
  );
985
1339
  stream.reset();
986
1340
 
987
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
988
- const wrap = N(M.Wrap);
1341
+ try log.moveFrom(p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome);
989
1342
  try expectLog1(
990
- &.{ 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) },
991
1344
  buf[0..6],
992
1345
  );
993
1346
  stream.reset();
994
1347
 
995
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1348
+ try log.move(p2.ident(4), M1.SkullBash, .{});
996
1349
  try log.laststill();
997
1350
  try expectLog1(
998
1351
  &.{
999
1352
  N(ArgType.Move),
1000
1353
  0b1100,
1001
- N(M.SkullBash),
1354
+ N(M1.SkullBash),
1002
1355
  0,
1003
1356
  N(Move.None),
1004
1357
  N(ArgType.LastStill),
@@ -1007,13 +1360,13 @@ test "|move|" {
1007
1360
  );
1008
1361
  stream.reset();
1009
1362
 
1010
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1363
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5));
1011
1364
  try log.lastmiss();
1012
1365
  try expectLog1(
1013
1366
  &.{
1014
1367
  N(ArgType.Move),
1015
1368
  0b1100,
1016
- N(M.Tackle),
1369
+ N(M1.Tackle),
1017
1370
  0b0101,
1018
1371
  N(Move.None),
1019
1372
  N(ArgType.LastMiss),
@@ -1032,8 +1385,8 @@ test "|switch|" {
1032
1385
  try log.switched(p2.ident(3), &snorlax);
1033
1386
  const par = 0b1000000;
1034
1387
  var expected: []const u8 = switch (endian) {
1035
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
1036
- .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 },
1037
1390
  };
1038
1391
  try expectLog1(expected, buf[0..9]);
1039
1392
  stream.reset();
@@ -1043,8 +1396,8 @@ test "|switch|" {
1043
1396
  snorlax.status = 0;
1044
1397
  try log.switched(p2.ident(3), &snorlax);
1045
1398
  expected = switch (endian) {
1046
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
1047
- .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 },
1048
1401
  };
1049
1402
  try expectLog1(expected, buf[0..9]);
1050
1403
  stream.reset();
@@ -1052,11 +1405,30 @@ test "|switch|" {
1052
1405
  snorlax.hp = 400;
1053
1406
  try log.switched(p2.ident(3), &snorlax);
1054
1407
  expected = switch (endian) {
1055
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
1056
- .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 },
1057
1410
  };
1058
1411
  try expectLog1(expected, buf[0..9]);
1059
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();
1060
1432
  }
1061
1433
 
1062
1434
  test "|cant|" {
@@ -1064,8 +1436,8 @@ test "|cant|" {
1064
1436
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1065
1437
  stream.reset();
1066
1438
 
1067
- try log.disabled(p1.ident(2), M.Earthquake);
1068
- 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]);
1069
1441
  stream.reset();
1070
1442
  }
1071
1443
 
@@ -1175,9 +1547,9 @@ test "|-status|" {
1175
1547
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1176
1548
  stream.reset();
1177
1549
 
1178
- 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);
1179
1551
  try expectLog1(
1180
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1552
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1181
1553
  buf[0..5],
1182
1554
  );
1183
1555
  stream.reset();
@@ -1248,8 +1620,8 @@ test "|-hitcount|" {
1248
1620
  }
1249
1621
 
1250
1622
  test "|-prepare|" {
1251
- try log.prepare(p2.ident(2), M.Dig);
1252
- 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]);
1253
1625
  stream.reset();
1254
1626
  }
1255
1627
 
@@ -1275,6 +1647,12 @@ test "|-activate|" {
1275
1647
  try log.activate(p1.ident(2), .Splash);
1276
1648
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1277
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();
1278
1656
  }
1279
1657
 
1280
1658
  test "|-fieldactivate|" {
@@ -1306,12 +1684,12 @@ test "|-start|" {
1306
1684
  );
1307
1685
  stream.reset();
1308
1686
 
1309
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1310
- 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]);
1311
1689
  stream.reset();
1312
1690
 
1313
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1314
- 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]);
1315
1693
  stream.reset();
1316
1694
  }
1317
1695
 
@@ -1330,21 +1708,25 @@ test "|-ohko|" {
1330
1708
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1331
1709
  stream.reset();
1332
1710
  }
1711
+
1333
1712
  test "|-crit|" {
1334
1713
  try log.crit(p2.ident(5));
1335
1714
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1336
1715
  stream.reset();
1337
1716
  }
1717
+
1338
1718
  test "|-supereffective|" {
1339
1719
  try log.supereffective(p1.ident(1));
1340
1720
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1341
1721
  stream.reset();
1342
1722
  }
1723
+
1343
1724
  test "|-resisted|" {
1344
1725
  try log.resisted(p2.ident(2));
1345
1726
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1346
1727
  stream.reset();
1347
1728
  }
1729
+
1348
1730
  test "|-immune|" {
1349
1731
  try log.immune(p1.ident(3), .None);
1350
1732
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1354,8 +1736,124 @@ test "|-immune|" {
1354
1736
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1355
1737
  stream.reset();
1356
1738
  }
1739
+
1357
1740
  test "|-transform|" {
1358
1741
  try log.transform(p2.ident(4), p1.ident(5));
1359
1742
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1360
1743
  stream.reset();
1361
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
+ }