@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.4c173f03

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,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,23 @@ 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
183
216
  };
184
217
 
185
218
  pub const Start = enum(u8) {
@@ -197,6 +230,19 @@ pub const Start = enum(u8) {
197
230
 
198
231
  Disable,
199
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,
200
246
  };
201
247
 
202
248
  pub const End = enum(u8) {
@@ -205,16 +251,27 @@ pub const End = enum(u8) {
205
251
  Bide,
206
252
  Substitute,
207
253
 
208
- // Silent
254
+ Nightmare,
255
+ Curse,
256
+ Foresight,
257
+ Encore,
258
+ FutureSight,
259
+ LeechSeed,
260
+ Bind,
261
+ Wrap,
262
+ FireSpin,
263
+ Clamp,
264
+ Whirlpool,
265
+
209
266
  DisableSilent,
210
267
  ConfusionSilent,
211
-
212
- Mist,
213
- FocusEnergy,
214
- LeechSeed,
215
- Toxic,
216
- LightScreen,
217
- Reflect,
268
+ MistSilent,
269
+ FocusEnergySilent,
270
+ LeechSeedSilent,
271
+ ToxicSilent,
272
+ LightScreenSilent,
273
+ ReflectSilent,
274
+ BideSilent,
218
275
  };
219
276
 
220
277
  pub const Immune = enum(u8) {
@@ -222,6 +279,28 @@ pub const Immune = enum(u8) {
222
279
  OHKO,
223
280
  };
224
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
+
225
304
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
226
305
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
227
306
  /// entirely with `options.log`.
@@ -236,7 +315,7 @@ pub fn Log(comptime Writer: type) type {
236
315
 
237
316
  pub const Error = Writer.Error;
238
317
 
239
- 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 {
240
319
  if (!enabled) return;
241
320
 
242
321
  assert(m != .None);
@@ -245,26 +324,43 @@ pub fn Log(comptime Writer: type) type {
245
324
  @as(u8, @bitCast(source)),
246
325
  @intFromEnum(m),
247
326
  @as(u8, @bitCast(target)),
327
+ @intFromEnum(Move.None),
248
328
  });
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
- },
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)),
260
340
  });
341
+ try self.writer.writeAll(if (@intFromEnum(from) == 0)
342
+ &.{@intFromEnum(Move.None)}
343
+ else
344
+ &.{ @intFromEnum(Move.From), @intFromEnum(from) });
261
345
  }
262
346
 
263
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 {
264
352
  if (!enabled) return;
265
353
 
266
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
267
- 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
+ }
268
364
  try self.writer.writeIntNative(u16, pokemon.hp);
269
365
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
270
366
  try self.writer.writeAll(&.{pokemon.status});
@@ -477,6 +573,7 @@ pub fn Log(comptime Writer: type) type {
477
573
  pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
478
574
  if (!enabled) return;
479
575
 
576
+ assert(reason != .SubstituteBlock); // TODO
480
577
  try self.writer.writeAll(&.{
481
578
  @intFromEnum(ArgType.Activate),
482
579
  @as(u8, @bitCast(ident)),
@@ -484,6 +581,18 @@ pub fn Log(comptime Writer: type) type {
484
581
  });
485
582
  }
486
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
+
487
596
  pub fn fieldactivate(self: Self) Error!void {
488
597
  if (!enabled) return;
489
598
 
@@ -585,6 +694,133 @@ pub fn Log(comptime Writer: type) type {
585
694
  });
586
695
  }
587
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
+
588
824
  pub fn laststill(self: Self) Error!void {
589
825
  if (!enabled) return;
590
826
 
@@ -645,10 +881,14 @@ pub const ByteStream = struct {
645
881
  }
646
882
  };
647
883
 
648
- pub const Kind = enum { Move, Species, Type, Status };
649
- pub const Formatter = fn (Kind, u8) []const u8;
884
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
650
885
 
651
- 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 {
652
892
  print("\n", .{});
653
893
 
654
894
  var i: usize = 0;
@@ -686,6 +926,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
686
926
  .Resisted => "|-resisted|",
687
927
  .Immune => "|-immune|",
688
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|",
689
941
  else => unreachable,
690
942
  };
691
943
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -701,17 +953,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
701
953
  .Move => {
702
954
  const source = ID.from(@intCast(a[i]));
703
955
  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);
956
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
705
957
  const target = ID.from(@intCast(a[i]));
706
958
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
707
959
  const reason: Move = @enumFromInt(a[i]);
708
960
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
709
- 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
+ }
710
964
  },
711
- .Switch => {
965
+ .Switch, .Drag => {
712
966
  const id = ID.from(@intCast(a[i]));
713
967
  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);
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
+ }
715
977
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
716
978
  switch (endian) {
717
979
  .Big => {
@@ -727,7 +989,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
727
989
  printc("/{d}", .{hp}, a, b, &i, 2, color);
728
990
  },
729
991
  }
730
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
992
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
731
993
  },
732
994
  .Cant => {
733
995
  const id = ID.from(@intCast(a[i]));
@@ -735,7 +997,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
735
997
  const reason: Cant = @enumFromInt(a[i]);
736
998
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
737
999
  if (reason == .Disable) {
738
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1000
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
739
1001
  }
740
1002
  },
741
1003
  .Faint,
@@ -744,6 +1006,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
744
1006
  .Crit,
745
1007
  .SuperEffective,
746
1008
  .Resisted,
1009
+ .CureTeam,
747
1010
  => {
748
1011
  const id = ID.from(@intCast(a[i]));
749
1012
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
@@ -775,7 +1038,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
775
1038
  printc("/{d}", .{hp}, a, b, &i, 2, color);
776
1039
  },
777
1040
  }
778
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1041
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
779
1042
  if (arg == .Damage) {
780
1043
  const reason: Damage = @enumFromInt(a[i]);
781
1044
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -795,15 +1058,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
795
1058
  .Status => {
796
1059
  const id = ID.from(@intCast(a[i]));
797
1060
  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);
1061
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
799
1062
  const reason: Status = @enumFromInt(a[i]);
800
1063
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
801
- 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
+ }
802
1067
  },
803
1068
  .CureStatus => {
804
1069
  const id = ID.from(@intCast(a[i]));
805
1070
  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);
1071
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
807
1072
  const reason: CureStatus = @enumFromInt(a[i]);
808
1073
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
809
1074
  },
@@ -818,15 +1083,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
818
1083
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
819
1084
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
820
1085
  },
821
- .HitCount => {
1086
+ .HitCount, .SetBoost => {
822
1087
  const id = ID.from(@intCast(a[i]));
823
1088
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
824
1089
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
825
1090
  },
826
- .Prepare => {
1091
+ .Prepare, .SingleMove, .SingleTurn => {
827
1092
  const id = ID.from(@intCast(a[i]));
828
1093
  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);
1094
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
830
1095
  },
831
1096
  .Activate => {
832
1097
  const id = ID.from(@intCast(a[i]));
@@ -841,12 +1106,12 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
841
1106
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
842
1107
  const types = @as(gen1.Types, @bitCast(a[i]));
843
1108
  const args = .{
844
- formatter(.Type, @intFromEnum(types.type1)),
845
- formatter(.Type, @intFromEnum(types.type2)),
1109
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1110
+ formatter(gen, .Type, @intFromEnum(types.type2)),
846
1111
  };
847
1112
  printc(" {s}/{s}", args, a, b, &i, 1, color);
848
1113
  } else if (reason >= @intFromEnum(Start.Disable)) {
849
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1114
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
850
1115
  }
851
1116
  },
852
1117
  .End => {
@@ -859,12 +1124,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
859
1124
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
860
1125
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
861
1126
  },
862
- .Transform => {
1127
+ .Transform, .CopyBoost => {
863
1128
  const source = ID.from(@intCast(a[i]));
864
1129
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
865
1130
  const target = ID.from(@intCast(a[i]));
866
1131
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
867
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
+ },
868
1185
  else => unreachable,
869
1186
  }
870
1187
  print("\n", .{});
@@ -896,7 +1213,7 @@ fn printc(
896
1213
  }
897
1214
 
898
1215
  pub fn expectLog(
899
- comptime formatter: Formatter,
1216
+ comptime gen: anytype,
900
1217
  expected: []const u8,
901
1218
  actual: []const u8,
902
1219
  offset: usize,
@@ -915,8 +1232,8 @@ pub fn expectLog(
915
1232
 
916
1233
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
917
1234
  error.TestExpectedEqual => {
918
- format(formatter, expected, null, color);
919
- format(formatter, actual, expected, color);
1235
+ format(gen, expected, null, color);
1236
+ format(gen, actual, expected, color);
920
1237
  return err;
921
1238
  },
922
1239
  else => return err,
@@ -956,49 +1273,70 @@ const gen1 = struct {
956
1273
  pub const helpers = @import("../gen1/helpers.zig");
957
1274
  };
958
1275
 
1276
+ const gen2 = struct {
1277
+ pub usingnamespace @import("../gen2/data.zig");
1278
+ pub const helpers = @import("../gen2/helpers.zig");
1279
+ };
1280
+
959
1281
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
960
- var stream = ByteStream{ .buffer = &buf };
1282
+ var stream: ByteStream = .{ .buffer = &buf };
961
1283
  var log: FixedLog = .{ .writer = stream.writer() };
962
1284
 
963
- const M = gen1.Move;
964
- 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;
965
1292
 
966
1293
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
967
- 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);
968
1299
  }
969
1300
 
970
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1301
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
971
1302
  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),
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,
976
1315
  };
977
1316
  }
978
1317
 
979
1318
  test "|move|" {
980
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1319
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5));
981
1320
  try expectLog1(
982
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1321
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
983
1322
  buf[0..5],
984
1323
  );
985
1324
  stream.reset();
986
1325
 
987
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
988
- const wrap = N(M.Wrap);
1326
+ try log.moveFrom(p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome);
989
1327
  try expectLog1(
990
- &.{ 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) },
991
1329
  buf[0..6],
992
1330
  );
993
1331
  stream.reset();
994
1332
 
995
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1333
+ try log.move(p2.ident(4), M1.SkullBash, .{});
996
1334
  try log.laststill();
997
1335
  try expectLog1(
998
1336
  &.{
999
1337
  N(ArgType.Move),
1000
1338
  0b1100,
1001
- N(M.SkullBash),
1339
+ N(M1.SkullBash),
1002
1340
  0,
1003
1341
  N(Move.None),
1004
1342
  N(ArgType.LastStill),
@@ -1007,13 +1345,13 @@ test "|move|" {
1007
1345
  );
1008
1346
  stream.reset();
1009
1347
 
1010
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1348
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5));
1011
1349
  try log.lastmiss();
1012
1350
  try expectLog1(
1013
1351
  &.{
1014
1352
  N(ArgType.Move),
1015
1353
  0b1100,
1016
- N(M.Tackle),
1354
+ N(M1.Tackle),
1017
1355
  0b0101,
1018
1356
  N(Move.None),
1019
1357
  N(ArgType.LastMiss),
@@ -1032,8 +1370,8 @@ test "|switch|" {
1032
1370
  try log.switched(p2.ident(3), &snorlax);
1033
1371
  const par = 0b1000000;
1034
1372
  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 },
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 },
1037
1375
  };
1038
1376
  try expectLog1(expected, buf[0..9]);
1039
1377
  stream.reset();
@@ -1043,8 +1381,8 @@ test "|switch|" {
1043
1381
  snorlax.status = 0;
1044
1382
  try log.switched(p2.ident(3), &snorlax);
1045
1383
  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 },
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 },
1048
1386
  };
1049
1387
  try expectLog1(expected, buf[0..9]);
1050
1388
  stream.reset();
@@ -1052,11 +1390,30 @@ test "|switch|" {
1052
1390
  snorlax.hp = 400;
1053
1391
  try log.switched(p2.ident(3), &snorlax);
1054
1392
  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 },
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 },
1057
1395
  };
1058
1396
  try expectLog1(expected, buf[0..9]);
1059
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();
1060
1417
  }
1061
1418
 
1062
1419
  test "|cant|" {
@@ -1064,8 +1421,8 @@ test "|cant|" {
1064
1421
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1065
1422
  stream.reset();
1066
1423
 
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]);
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]);
1069
1426
  stream.reset();
1070
1427
  }
1071
1428
 
@@ -1175,9 +1532,9 @@ test "|-status|" {
1175
1532
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1176
1533
  stream.reset();
1177
1534
 
1178
- 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);
1179
1536
  try expectLog1(
1180
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1537
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1181
1538
  buf[0..5],
1182
1539
  );
1183
1540
  stream.reset();
@@ -1248,8 +1605,8 @@ test "|-hitcount|" {
1248
1605
  }
1249
1606
 
1250
1607
  test "|-prepare|" {
1251
- try log.prepare(p2.ident(2), M.Dig);
1252
- 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]);
1253
1610
  stream.reset();
1254
1611
  }
1255
1612
 
@@ -1306,12 +1663,12 @@ test "|-start|" {
1306
1663
  );
1307
1664
  stream.reset();
1308
1665
 
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]);
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]);
1311
1668
  stream.reset();
1312
1669
 
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]);
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]);
1315
1672
  stream.reset();
1316
1673
  }
1317
1674
 
@@ -1330,21 +1687,25 @@ test "|-ohko|" {
1330
1687
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1331
1688
  stream.reset();
1332
1689
  }
1690
+
1333
1691
  test "|-crit|" {
1334
1692
  try log.crit(p2.ident(5));
1335
1693
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1336
1694
  stream.reset();
1337
1695
  }
1696
+
1338
1697
  test "|-supereffective|" {
1339
1698
  try log.supereffective(p1.ident(1));
1340
1699
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1341
1700
  stream.reset();
1342
1701
  }
1702
+
1343
1703
  test "|-resisted|" {
1344
1704
  try log.resisted(p2.ident(2));
1345
1705
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1346
1706
  stream.reset();
1347
1707
  }
1708
+
1348
1709
  test "|-immune|" {
1349
1710
  try log.immune(p1.ident(3), .None);
1350
1711
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1354,8 +1715,125 @@ test "|-immune|" {
1354
1715
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1355
1716
  stream.reset();
1356
1717
  }
1718
+
1357
1719
  test "|-transform|" {
1358
1720
  try log.transform(p2.ident(4), p1.ident(5));
1359
1721
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1360
1722
  stream.reset();
1361
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
+ }