@pkmn/engine 0.1.0-dev.f3aaee39 → 0.1.0-dev.f918c3bd

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.
@@ -121,12 +121,16 @@ 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,
130
134
  };
131
135
 
132
136
  pub const Damage = enum(u8) {
@@ -136,6 +140,13 @@ pub const Damage = enum(u8) {
136
140
  Confusion,
137
141
  LeechSeed,
138
142
  RecoilOf,
143
+
144
+ Bind,
145
+ Wrap,
146
+ FireSpin,
147
+ Clamp,
148
+ Whirlpool,
149
+ Spikes,
139
150
  };
140
151
 
141
152
  pub const Status = enum(u8) {
@@ -180,6 +191,16 @@ pub const Activate = enum(u8) {
180
191
  Struggle,
181
192
  Substitute,
182
193
  Splash,
194
+
195
+ Attract,
196
+ LockOn,
197
+ Bind,
198
+ Wrap,
199
+ FireSpin,
200
+ Clamp,
201
+ Whirlpool,
202
+
203
+ BeatUp, // FIXME of
183
204
  };
184
205
 
185
206
  pub const Start = enum(u8) {
@@ -197,6 +218,18 @@ pub const Start = enum(u8) {
197
218
 
198
219
  Disable,
199
220
  Mimic,
221
+
222
+ Nightmare,
223
+ Curse,
224
+ Foresight,
225
+ Encore,
226
+ FutureSight,
227
+
228
+ PerishSong3Silent,
229
+ PerishSong3,
230
+ PerishSong2,
231
+ PerishSong1,
232
+ PerishSong0,
200
233
  };
201
234
 
202
235
  pub const End = enum(u8) {
@@ -205,16 +238,27 @@ pub const End = enum(u8) {
205
238
  Bide,
206
239
  Substitute,
207
240
 
208
- // Silent
241
+ Nightmare,
242
+ Curse,
243
+ Foresight,
244
+ Encore,
245
+ FutureSight,
246
+ LeechSeed,
247
+ Bind,
248
+ Wrap,
249
+ FireSpin,
250
+ Clamp,
251
+ Whirlpool,
252
+
209
253
  DisableSilent,
210
254
  ConfusionSilent,
211
-
212
- Mist,
213
- FocusEnergy,
214
- LeechSeed,
215
- Toxic,
216
- LightScreen,
217
- Reflect,
255
+ MistSilent,
256
+ FocusEnergySilent,
257
+ LeechSeedSilent,
258
+ ToxicSilent,
259
+ LightScreenSilent,
260
+ ReflectSilent,
261
+ BideSilent,
218
262
  };
219
263
 
220
264
  pub const Immune = enum(u8) {
@@ -222,6 +266,28 @@ pub const Immune = enum(u8) {
222
266
  OHKO,
223
267
  };
224
268
 
269
+ pub const EndItem = enum(u8) {
270
+ None,
271
+ Eat,
272
+ };
273
+
274
+ pub const SetHP = enum(u8) {
275
+ None,
276
+ Silent,
277
+ };
278
+
279
+ pub const Side = enum(u8) {
280
+ Safeguard,
281
+ Reflect,
282
+ LightScreen,
283
+ Spikes,
284
+ };
285
+
286
+ pub const Weather = enum(u8) {
287
+ None,
288
+ Upkeep,
289
+ };
290
+
225
291
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
226
292
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
227
293
  /// entirely with `options.log`.
@@ -261,10 +327,22 @@ pub fn Log(comptime Writer: type) type {
261
327
  }
262
328
 
263
329
  pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
330
+ return switchDrag(self, .Switch, ident, pokemon);
331
+ }
332
+
333
+ fn switchDrag(self: Self, arg: ArgType, ident: ID, pokemon: anytype) Error!void {
264
334
  if (!enabled) return;
265
335
 
266
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
267
- try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
336
+ try self.writer.writeAll(&.{ @intFromEnum(arg), @as(u8, @bitCast(ident)) });
337
+ if (@hasField(@TypeOf(pokemon.*), "dvs")) {
338
+ try self.writer.writeAll(&.{
339
+ @intFromEnum(pokemon.species),
340
+ @intFromEnum(pokemon.dvs.gender),
341
+ pokemon.level,
342
+ });
343
+ } else {
344
+ try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
345
+ }
268
346
  try self.writer.writeIntNative(u16, pokemon.hp);
269
347
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
270
348
  try self.writer.writeAll(&.{pokemon.status});
@@ -585,6 +663,133 @@ pub fn Log(comptime Writer: type) type {
585
663
  });
586
664
  }
587
665
 
666
+ pub fn drag(self: Self, ident: ID, pokemon: anytype) Error!void {
667
+ return switchDrag(self, .Drag, ident, pokemon);
668
+ }
669
+
670
+ pub fn item(self: Self, target: ID, i: anytype, source: ID) Error!void {
671
+ if (!enabled) return;
672
+
673
+ try self.writer.writeAll(&.{
674
+ @intFromEnum(ArgType.Item),
675
+ @as(u8, @bitCast(target)),
676
+ @intFromEnum(i),
677
+ @as(u8, @bitCast(source)),
678
+ });
679
+ }
680
+
681
+ pub fn enditem(self: Self, ident: ID, i: anytype, reason: EndItem) Error!void {
682
+ if (!enabled) return;
683
+
684
+ try self.writer.writeAll(&.{
685
+ @intFromEnum(ArgType.EndItem),
686
+ @as(u8, @bitCast(ident)),
687
+ @intFromEnum(i),
688
+ @intFromEnum(reason),
689
+ });
690
+ }
691
+
692
+ pub fn cureteam(self: Self, source: ID) Error!void {
693
+ if (!enabled) return;
694
+
695
+ try self.writer.writeAll(&.{
696
+ @intFromEnum(ArgType.CureTeam),
697
+ @as(u8, @bitCast(source)),
698
+ });
699
+ }
700
+
701
+ pub fn sethp(self: Self, ident: ID, pokemon: anytype, reason: SetHP) Error!void {
702
+ if (!enabled) return;
703
+
704
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(ident)) });
705
+ try self.writer.writeIntNative(u16, pokemon.hp);
706
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
707
+ try self.writer.writeAll(&.{pokemon.status});
708
+ try self.writer.writeByte(@intFromEnum(reason));
709
+ }
710
+
711
+ pub fn setboost(self: Self, ident: ID, num: i8) Error!void {
712
+ if (!enabled) return;
713
+
714
+ try self.writer.writeAll(&.{
715
+ @intFromEnum(ArgType.SetBoost),
716
+ @as(u8, @bitCast(ident)),
717
+ @as(u8, @intCast(num + 6)),
718
+ });
719
+ }
720
+
721
+ pub fn copyboost(self: Self, source: ID, target: ID) Error!void {
722
+ if (!enabled) return;
723
+
724
+ try self.writer.writeAll(&.{
725
+ @intFromEnum(ArgType.CopyBoost),
726
+ @as(u8, @bitCast(source)),
727
+ @as(u8, @bitCast(target)),
728
+ });
729
+ }
730
+
731
+ pub fn sidestart(self: Self, player: Player, reason: Side) Error!void {
732
+ if (!enabled) return;
733
+
734
+ try self.writer.writeAll(&.{
735
+ @intFromEnum(ArgType.SideStart),
736
+ @intFromEnum(player),
737
+ @intFromEnum(reason),
738
+ });
739
+ }
740
+
741
+ pub fn sideend(self: Self, player: Player, reason: Side) Error!void {
742
+ if (!enabled) return;
743
+
744
+ assert(@intFromEnum(reason) != @intFromEnum(Side.Spikes));
745
+ try self.writer.writeAll(&.{
746
+ @intFromEnum(ArgType.SideEnd),
747
+ @intFromEnum(player),
748
+ @intFromEnum(reason),
749
+ });
750
+ }
751
+
752
+ pub fn spikesend(self: Self, player: Player, m: anytype, source: ID) Error!void {
753
+ if (!enabled) return;
754
+
755
+ try self.writer.writeAll(&.{
756
+ @intFromEnum(ArgType.SideEnd),
757
+ @intFromEnum(player),
758
+ @intFromEnum(Side.Spikes),
759
+ @intFromEnum(m),
760
+ @as(u8, @bitCast(source)),
761
+ });
762
+ }
763
+
764
+ pub fn singlemove(self: Self, ident: ID, m: anytype) Error!void {
765
+ return single(self, .SingleMove, ident, m);
766
+ }
767
+
768
+ pub fn singleturn(self: Self, ident: ID, m: anytype) Error!void {
769
+ return single(self, .SingleTurn, ident, m);
770
+ }
771
+
772
+ fn single(self: Self, arg: ArgType, ident: ID, m: anytype) Error!void {
773
+ if (!enabled) return;
774
+
775
+ try self.writer.writeAll(&.{
776
+ @intFromEnum(arg),
777
+ @as(u8, @bitCast(ident)),
778
+ @intFromEnum(m),
779
+ });
780
+ }
781
+
782
+ pub fn weather(self: Self, w: anytype, reason: Weather) Error!void {
783
+ if (!enabled) return;
784
+
785
+ assert(!(w == .None and reason == .Upkeep));
786
+ try self.writer.writeAll(&.{
787
+ @intFromEnum(ArgType.Weather),
788
+ @intFromEnum(w),
789
+ @intFromEnum(reason),
790
+ });
791
+ }
792
+
588
793
  pub fn laststill(self: Self) Error!void {
589
794
  if (!enabled) return;
590
795
 
@@ -645,10 +850,14 @@ pub const ByteStream = struct {
645
850
  }
646
851
  };
647
852
 
648
- pub const Kind = enum { Move, Species, Type, Status };
649
- pub const Formatter = fn (Kind, u8) []const u8;
853
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
650
854
 
651
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
855
+ pub fn format(
856
+ comptime gen: anytype,
857
+ a: []const u8,
858
+ b: ?[]const u8,
859
+ color: bool,
860
+ ) void {
652
861
  print("\n", .{});
653
862
 
654
863
  var i: usize = 0;
@@ -686,6 +895,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
686
895
  .Resisted => "|-resisted|",
687
896
  .Immune => "|-immune|",
688
897
  .Transform => "|-transform|",
898
+ .Drag => "|drag|",
899
+ .Item => "|-item|",
900
+ .EndItem => "|-enditem|",
901
+ .CureTeam => "|-cureteam|",
902
+ .SetHP => "|-sethp|",
903
+ .SetBoost => "|-setboost|",
904
+ .CopyBoost => "|-copyboost|",
905
+ .SideStart => "|-sidestart|",
906
+ .SideEnd => "|-sideend|",
907
+ .SingleMove => "|-singlemove|",
908
+ .SingleTurn => "|-singleturn|",
909
+ .Weather => "|-weather|",
689
910
  else => unreachable,
690
911
  };
691
912
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -701,17 +922,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
701
922
  .Move => {
702
923
  const source = ID.from(@intCast(a[i]));
703
924
  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);
925
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
705
926
  const target = ID.from(@intCast(a[i]));
706
927
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
707
928
  const reason: Move = @enumFromInt(a[i]);
708
929
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
709
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
930
+ if (reason == .From) {
931
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
932
+ }
710
933
  },
711
- .Switch => {
934
+ .Switch, .Drag => {
712
935
  const id = ID.from(@intCast(a[i]));
713
936
  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);
937
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
938
+ if (@hasDecl(gen, "Gender")) {
939
+ if (a[i] == N(gen.Gender.Unknown)) {
940
+ i += 1;
941
+ } else {
942
+ var gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
943
+ printc(", {s}", .{gender}, a, b, &i, 1, color);
944
+ }
945
+ }
715
946
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
716
947
  switch (endian) {
717
948
  .Big => {
@@ -727,7 +958,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
727
958
  printc("/{d}", .{hp}, a, b, &i, 2, color);
728
959
  },
729
960
  }
730
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
961
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
731
962
  },
732
963
  .Cant => {
733
964
  const id = ID.from(@intCast(a[i]));
@@ -735,7 +966,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
735
966
  const reason: Cant = @enumFromInt(a[i]);
736
967
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
737
968
  if (reason == .Disable) {
738
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
969
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
739
970
  }
740
971
  },
741
972
  .Faint,
@@ -744,6 +975,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
744
975
  .Crit,
745
976
  .SuperEffective,
746
977
  .Resisted,
978
+ .CureTeam,
747
979
  => {
748
980
  const id = ID.from(@intCast(a[i]));
749
981
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
@@ -775,7 +1007,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
775
1007
  printc("/{d}", .{hp}, a, b, &i, 2, color);
776
1008
  },
777
1009
  }
778
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1010
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
779
1011
  if (arg == .Damage) {
780
1012
  const reason: Damage = @enumFromInt(a[i]);
781
1013
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -795,15 +1027,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
795
1027
  .Status => {
796
1028
  const id = ID.from(@intCast(a[i]));
797
1029
  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);
1030
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
799
1031
  const reason: Status = @enumFromInt(a[i]);
800
1032
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
801
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1033
+ if (reason == .From) {
1034
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1035
+ }
802
1036
  },
803
1037
  .CureStatus => {
804
1038
  const id = ID.from(@intCast(a[i]));
805
1039
  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);
1040
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
807
1041
  const reason: CureStatus = @enumFromInt(a[i]);
808
1042
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
809
1043
  },
@@ -818,15 +1052,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
818
1052
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
819
1053
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
820
1054
  },
821
- .HitCount => {
1055
+ .HitCount, .SetBoost => {
822
1056
  const id = ID.from(@intCast(a[i]));
823
1057
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
824
1058
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
825
1059
  },
826
- .Prepare => {
1060
+ .Prepare, .SingleMove, .SingleTurn => {
827
1061
  const id = ID.from(@intCast(a[i]));
828
1062
  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);
1063
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
830
1064
  },
831
1065
  .Activate => {
832
1066
  const id = ID.from(@intCast(a[i]));
@@ -841,12 +1075,12 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
841
1075
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
842
1076
  const types = @as(gen1.Types, @bitCast(a[i]));
843
1077
  const args = .{
844
- formatter(.Type, @intFromEnum(types.type1)),
845
- formatter(.Type, @intFromEnum(types.type2)),
1078
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1079
+ formatter(gen, .Type, @intFromEnum(types.type2)),
846
1080
  };
847
1081
  printc(" {s}/{s}", args, a, b, &i, 1, color);
848
1082
  } else if (reason >= @intFromEnum(Start.Disable)) {
849
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1083
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
850
1084
  }
851
1085
  },
852
1086
  .End => {
@@ -859,12 +1093,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
859
1093
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
860
1094
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
861
1095
  },
862
- .Transform => {
1096
+ .Transform, .CopyBoost => {
863
1097
  const source = ID.from(@intCast(a[i]));
864
1098
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
865
1099
  const target = ID.from(@intCast(a[i]));
866
1100
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
867
1101
  },
1102
+ .Item, .EndItem => {
1103
+ var id = ID.from(@intCast(a[i]));
1104
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1105
+ printc(" {s}", .{formatter(gen, .Item, a[i])}, a, b, &i, 1, color);
1106
+ if (arg == .Item) {
1107
+ id = ID.from(@intCast(a[i]));
1108
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1109
+ } else {
1110
+ const reason: EndItem = @enumFromInt(a[i]);
1111
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1112
+ }
1113
+ },
1114
+ .SetHP => {
1115
+ const id = ID.from(@intCast(a[i]));
1116
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1117
+ switch (endian) {
1118
+ .Big => {
1119
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1120
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1121
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1122
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1123
+ },
1124
+ .Little => {
1125
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1126
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1127
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1128
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1129
+ },
1130
+ }
1131
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1132
+ printc(" {s}", .{@tagName(@as(SetHP, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1133
+ },
1134
+ .SideStart => {
1135
+ const id = ID.from(@intCast(a[i]));
1136
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1137
+ printc(" {s}", .{@tagName(@as(Side, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1138
+ },
1139
+ .SideEnd => {
1140
+ var id = ID.from(@intCast(a[i]));
1141
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1142
+ const reason: Side = @enumFromInt(a[i]);
1143
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1144
+ if (reason == .Spikes) {
1145
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1146
+ id = ID.from(@intCast(a[i]));
1147
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1148
+ }
1149
+ },
1150
+ .Weather => {
1151
+ printc(" {s}", .{formatter(gen, .Weather, a[i])}, a, b, &i, 1, color);
1152
+ printc(" {s}", .{@tagName(@as(Weather, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1153
+ },
868
1154
  else => unreachable,
869
1155
  }
870
1156
  print("\n", .{});
@@ -896,7 +1182,7 @@ fn printc(
896
1182
  }
897
1183
 
898
1184
  pub fn expectLog(
899
- comptime formatter: Formatter,
1185
+ comptime gen: anytype,
900
1186
  expected: []const u8,
901
1187
  actual: []const u8,
902
1188
  offset: usize,
@@ -915,8 +1201,8 @@ pub fn expectLog(
915
1201
 
916
1202
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
917
1203
  error.TestExpectedEqual => {
918
- format(formatter, expected, null, color);
919
- format(formatter, actual, expected, color);
1204
+ format(gen, expected, null, color);
1205
+ format(gen, actual, expected, color);
920
1206
  return err;
921
1207
  },
922
1208
  else => return err,
@@ -956,49 +1242,71 @@ const gen1 = struct {
956
1242
  pub const helpers = @import("../gen1/helpers.zig");
957
1243
  };
958
1244
 
1245
+ const gen2 = struct {
1246
+ pub usingnamespace @import("../gen2/data.zig");
1247
+ pub const helpers = @import("../gen2/helpers.zig");
1248
+ };
1249
+
959
1250
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
960
- var stream = ByteStream{ .buffer = &buf };
1251
+ var stream: ByteStream = .{ .buffer = &buf };
961
1252
  var log: FixedLog = .{ .writer = stream.writer() };
962
1253
 
963
- const M = gen1.Move;
964
- const S = gen1.Species;
1254
+ const M1 = gen1.Move;
1255
+ const S1 = gen1.Species;
1256
+
1257
+ const M2 = gen2.Move;
1258
+ const S2 = gen2.Species;
1259
+ const I2 = gen2.Item;
1260
+ const W2 = gen2.Weather;
965
1261
 
966
1262
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
967
- return expectLog(gen1Formatter, expected, actual, 0);
1263
+ return expectLog(gen1, expected, actual, 0);
968
1264
  }
969
1265
 
970
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1266
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1267
+ return expectLog(gen2, expected, actual, 0);
1268
+ }
1269
+
1270
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
971
1271
  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),
1272
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1273
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1274
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1275
+ .Status => gen.Status.name(byte),
1276
+ .Item => if (@hasDecl(gen, "Item"))
1277
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1278
+ else
1279
+ unreachable,
1280
+ .Weather => if (@hasDecl(gen, "Weather"))
1281
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1282
+ else
1283
+ unreachable,
976
1284
  };
977
1285
  }
978
1286
 
979
1287
  test "|move|" {
980
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1288
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5), null);
981
1289
  try expectLog1(
982
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1290
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
983
1291
  buf[0..5],
984
1292
  );
985
1293
  stream.reset();
986
1294
 
987
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
988
- const wrap = N(M.Wrap);
1295
+ try log.move(p2.ident(4), M1.Wrap, p1.ident(5), M1.Wrap);
1296
+ const wrap = N(M1.Wrap);
989
1297
  try expectLog1(
990
1298
  &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
991
1299
  buf[0..6],
992
1300
  );
993
1301
  stream.reset();
994
1302
 
995
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1303
+ try log.move(p2.ident(4), M1.SkullBash, .{}, null);
996
1304
  try log.laststill();
997
1305
  try expectLog1(
998
1306
  &.{
999
1307
  N(ArgType.Move),
1000
1308
  0b1100,
1001
- N(M.SkullBash),
1309
+ N(M1.SkullBash),
1002
1310
  0,
1003
1311
  N(Move.None),
1004
1312
  N(ArgType.LastStill),
@@ -1007,13 +1315,13 @@ test "|move|" {
1007
1315
  );
1008
1316
  stream.reset();
1009
1317
 
1010
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1318
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5), null);
1011
1319
  try log.lastmiss();
1012
1320
  try expectLog1(
1013
1321
  &.{
1014
1322
  N(ArgType.Move),
1015
1323
  0b1100,
1016
- N(M.Tackle),
1324
+ N(M1.Tackle),
1017
1325
  0b0101,
1018
1326
  N(Move.None),
1019
1327
  N(ArgType.LastMiss),
@@ -1032,8 +1340,8 @@ test "|switch|" {
1032
1340
  try log.switched(p2.ident(3), &snorlax);
1033
1341
  const par = 0b1000000;
1034
1342
  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 },
1343
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1344
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1037
1345
  };
1038
1346
  try expectLog1(expected, buf[0..9]);
1039
1347
  stream.reset();
@@ -1043,8 +1351,8 @@ test "|switch|" {
1043
1351
  snorlax.status = 0;
1044
1352
  try log.switched(p2.ident(3), &snorlax);
1045
1353
  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 },
1354
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1355
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1048
1356
  };
1049
1357
  try expectLog1(expected, buf[0..9]);
1050
1358
  stream.reset();
@@ -1052,11 +1360,30 @@ test "|switch|" {
1052
1360
  snorlax.hp = 400;
1053
1361
  try log.switched(p2.ident(3), &snorlax);
1054
1362
  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 },
1363
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1364
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1057
1365
  };
1058
1366
  try expectLog1(expected, buf[0..9]);
1059
1367
  stream.reset();
1368
+
1369
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1370
+ blissey.level = 91;
1371
+ blissey.hp = 200;
1372
+ blissey.stats.hp = 400;
1373
+ blissey.status = gen2.Status.init(.PAR);
1374
+ try log.switched(p2.ident(3), &blissey);
1375
+ expected = &(.{
1376
+ N(ArgType.Switch),
1377
+ 0b1011,
1378
+ N(S2.Blissey),
1379
+ N(gen2.Gender.Female),
1380
+ 91,
1381
+ } ++ switch (endian) {
1382
+ .Big => .{ 0, 200, 1, 144, par },
1383
+ .Little => .{ 200, 0, 144, 1, par },
1384
+ });
1385
+ try expectLog2(expected, buf[0..10]);
1386
+ stream.reset();
1060
1387
  }
1061
1388
 
1062
1389
  test "|cant|" {
@@ -1064,8 +1391,8 @@ test "|cant|" {
1064
1391
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1065
1392
  stream.reset();
1066
1393
 
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]);
1394
+ try log.disabled(p1.ident(2), M1.Earthquake);
1395
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1069
1396
  stream.reset();
1070
1397
  }
1071
1398
 
@@ -1175,9 +1502,9 @@ test "|-status|" {
1175
1502
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1176
1503
  stream.reset();
1177
1504
 
1178
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1505
+ try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M1.BodySlam);
1179
1506
  try expectLog1(
1180
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1507
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1181
1508
  buf[0..5],
1182
1509
  );
1183
1510
  stream.reset();
@@ -1248,8 +1575,8 @@ test "|-hitcount|" {
1248
1575
  }
1249
1576
 
1250
1577
  test "|-prepare|" {
1251
- try log.prepare(p2.ident(2), M.Dig);
1252
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1578
+ try log.prepare(p2.ident(2), M1.Dig);
1579
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1253
1580
  stream.reset();
1254
1581
  }
1255
1582
 
@@ -1306,12 +1633,12 @@ test "|-start|" {
1306
1633
  );
1307
1634
  stream.reset();
1308
1635
 
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]);
1636
+ try log.startEffect(p1.ident(2), .Disable, M1.Surf);
1637
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1311
1638
  stream.reset();
1312
1639
 
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]);
1640
+ try log.startEffect(p1.ident(2), .Mimic, M1.Surf);
1641
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1315
1642
  stream.reset();
1316
1643
  }
1317
1644
 
@@ -1330,21 +1657,25 @@ test "|-ohko|" {
1330
1657
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1331
1658
  stream.reset();
1332
1659
  }
1660
+
1333
1661
  test "|-crit|" {
1334
1662
  try log.crit(p2.ident(5));
1335
1663
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1336
1664
  stream.reset();
1337
1665
  }
1666
+
1338
1667
  test "|-supereffective|" {
1339
1668
  try log.supereffective(p1.ident(1));
1340
1669
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1341
1670
  stream.reset();
1342
1671
  }
1672
+
1343
1673
  test "|-resisted|" {
1344
1674
  try log.resisted(p2.ident(2));
1345
1675
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1346
1676
  stream.reset();
1347
1677
  }
1678
+
1348
1679
  test "|-immune|" {
1349
1680
  try log.immune(p1.ident(3), .None);
1350
1681
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1354,8 +1685,125 @@ test "|-immune|" {
1354
1685
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1355
1686
  stream.reset();
1356
1687
  }
1688
+
1357
1689
  test "|-transform|" {
1358
1690
  try log.transform(p2.ident(4), p1.ident(5));
1359
1691
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1360
1692
  stream.reset();
1361
1693
  }
1694
+
1695
+ test "|drag|" {
1696
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1697
+ blissey.level = 91;
1698
+ blissey.hp = 200;
1699
+ blissey.stats.hp = 400;
1700
+ blissey.status = gen2.Status.init(.PAR);
1701
+ try log.drag(p2.ident(3), &blissey);
1702
+ const par = 0b1000000;
1703
+ const expected = &(.{
1704
+ N(ArgType.Drag),
1705
+ 0b1011,
1706
+ N(S2.Blissey),
1707
+ N(gen2.Gender.Female),
1708
+ 91,
1709
+ } ++ switch (endian) {
1710
+ .Big => .{ 0, 200, 1, 144, par },
1711
+ .Little => .{ 200, 0, 144, 1, par },
1712
+ });
1713
+ try expectLog2(expected, buf[0..10]);
1714
+ stream.reset();
1715
+ }
1716
+
1717
+ test "|-item|" {
1718
+ try log.item(p2.ident(2), I2.GoldBerry, p1.ident(4));
1719
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1720
+ stream.reset();
1721
+ }
1722
+
1723
+ test "|-enditem|" {
1724
+ try log.enditem(p1.ident(1), I2.BerserkGene, .None);
1725
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1726
+ stream.reset();
1727
+
1728
+ try log.enditem(p2.ident(4), I2.PRZCureBerry, .Eat);
1729
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1730
+ stream.reset();
1731
+ }
1732
+
1733
+ test "|-cureteam|" {
1734
+ try log.cureteam(p2.ident(5));
1735
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1736
+ stream.reset();
1737
+ }
1738
+
1739
+ test "|-sethp|" {
1740
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1741
+ blissey.hp = 612;
1742
+ try log.sethp(p2.ident(2), &blissey, .Silent);
1743
+ var expected: []const u8 = switch (endian) {
1744
+ .Big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1745
+ .Little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1746
+ };
1747
+ try expectLog2(expected, buf[0..8]);
1748
+ stream.reset();
1749
+ }
1750
+
1751
+ test "|-setboost|" {
1752
+ try log.setboost(p2.ident(6), 6);
1753
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1754
+ stream.reset();
1755
+
1756
+ try log.setboost(p2.ident(3), 2);
1757
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1758
+ stream.reset();
1759
+ }
1760
+
1761
+ test "|-copyboost|" {
1762
+ try log.copyboost(p2.ident(3), p1.ident(6));
1763
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1764
+ stream.reset();
1765
+ }
1766
+
1767
+ test "|-sidestart|" {
1768
+ try log.sidestart(.P2, .Reflect);
1769
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1770
+ stream.reset();
1771
+ }
1772
+
1773
+ test "|-sideend|" {
1774
+ try log.sideend(.P2, .LightScreen);
1775
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1776
+ stream.reset();
1777
+
1778
+ try log.spikesend(.P1, M2.RapidSpin, p1.ident(3));
1779
+ try expectLog2(&.{
1780
+ N(ArgType.SideEnd),
1781
+ 0,
1782
+ N(Side.Spikes),
1783
+ N(M2.RapidSpin),
1784
+ 0b0011,
1785
+ }, buf[0..5]);
1786
+ stream.reset();
1787
+ }
1788
+
1789
+ test "|-singlemove|" {
1790
+ try log.singlemove(p2.ident(2), M2.DestinyBond);
1791
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1792
+ stream.reset();
1793
+ }
1794
+
1795
+ test "|-singleturn|" {
1796
+ try log.singleturn(p1.ident(3), M2.Protect);
1797
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1798
+ stream.reset();
1799
+ }
1800
+
1801
+ test "|-weather|" {
1802
+ try log.weather(W2.Rain, .Upkeep);
1803
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1804
+ stream.reset();
1805
+
1806
+ try log.weather(W2.None, .None);
1807
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1808
+ stream.reset();
1809
+ }