@pkmn/engine 0.1.0-dev.04c4d944 → 0.1.0-dev.1336408

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,13 @@ 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,
139
153
  };
140
154
 
141
155
  pub const Status = enum(u8) {
@@ -180,6 +194,18 @@ pub const Activate = enum(u8) {
180
194
  Struggle,
181
195
  Substitute,
182
196
  Splash,
197
+
198
+ Rage, // TODO
199
+
200
+ Attract,
201
+ LockOn,
202
+ Bind,
203
+ Wrap,
204
+ FireSpin,
205
+ Clamp,
206
+ Whirlpool,
207
+
208
+ BeatUp, // FIXME of
183
209
  };
184
210
 
185
211
  pub const Start = enum(u8) {
@@ -197,6 +223,19 @@ pub const Start = enum(u8) {
197
223
 
198
224
  Disable,
199
225
  Mimic,
226
+
227
+ Attract,
228
+ Nightmare,
229
+ Curse,
230
+ Foresight,
231
+ Encore,
232
+ FutureSight,
233
+
234
+ PerishSong0,
235
+ PerishSong1,
236
+ PerishSong2,
237
+ PerishSong3,
238
+ PerishSong3Silent,
200
239
  };
201
240
 
202
241
  pub const End = enum(u8) {
@@ -205,16 +244,27 @@ pub const End = enum(u8) {
205
244
  Bide,
206
245
  Substitute,
207
246
 
208
- // Silent
247
+ Nightmare,
248
+ Curse,
249
+ Foresight,
250
+ Encore,
251
+ FutureSight,
252
+ LeechSeed,
253
+ Bind,
254
+ Wrap,
255
+ FireSpin,
256
+ Clamp,
257
+ Whirlpool,
258
+
209
259
  DisableSilent,
210
260
  ConfusionSilent,
211
-
212
- Mist,
213
- FocusEnergy,
214
- LeechSeed,
215
- Toxic,
216
- LightScreen,
217
- Reflect,
261
+ MistSilent,
262
+ FocusEnergySilent,
263
+ LeechSeedSilent,
264
+ ToxicSilent,
265
+ LightScreenSilent,
266
+ ReflectSilent,
267
+ BideSilent,
218
268
  };
219
269
 
220
270
  pub const Immune = enum(u8) {
@@ -222,6 +272,28 @@ pub const Immune = enum(u8) {
222
272
  OHKO,
223
273
  };
224
274
 
275
+ pub const EndItem = enum(u8) {
276
+ None,
277
+ Eat,
278
+ };
279
+
280
+ pub const SetHP = enum(u8) {
281
+ None,
282
+ Silent,
283
+ };
284
+
285
+ pub const Side = enum(u8) {
286
+ Safeguard,
287
+ Reflect,
288
+ LightScreen,
289
+ Spikes,
290
+ };
291
+
292
+ pub const Weather = enum(u8) {
293
+ None,
294
+ Upkeep,
295
+ };
296
+
225
297
  /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
226
298
  /// Ignores anything sent to it, though protocol logging should additionally be turned off
227
299
  /// entirely with `options.log`.
@@ -236,7 +308,7 @@ pub fn Log(comptime Writer: type) type {
236
308
 
237
309
  pub const Error = Writer.Error;
238
310
 
239
- pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
311
+ pub fn move(self: Self, source: ID, m: anytype, target: ID) Error!void {
240
312
  if (!enabled) return;
241
313
 
242
314
  assert(m != .None);
@@ -245,26 +317,43 @@ pub fn Log(comptime Writer: type) type {
245
317
  @as(u8, @bitCast(source)),
246
318
  @intFromEnum(m),
247
319
  @as(u8, @bitCast(target)),
320
+ @intFromEnum(Move.None),
248
321
  });
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
- },
322
+ }
323
+
324
+ pub fn moveFrom(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
325
+ if (!enabled) return;
326
+
327
+ assert(m != .None);
328
+ try self.writer.writeAll(&.{
329
+ @intFromEnum(ArgType.Move),
330
+ @as(u8, @bitCast(source)),
331
+ @intFromEnum(m),
332
+ @as(u8, @bitCast(target)),
260
333
  });
334
+ try self.writer.writeAll(if (@intFromEnum(from) == 0)
335
+ &.{@intFromEnum(Move.None)}
336
+ else
337
+ &.{ @intFromEnum(Move.From), @intFromEnum(from) });
261
338
  }
262
339
 
263
340
  pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
341
+ return switchDrag(self, .Switch, ident, pokemon);
342
+ }
343
+
344
+ fn switchDrag(self: Self, arg: ArgType, ident: ID, pokemon: anytype) Error!void {
264
345
  if (!enabled) return;
265
346
 
266
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
267
- try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
347
+ try self.writer.writeAll(&.{ @intFromEnum(arg), @as(u8, @bitCast(ident)) });
348
+ if (@hasField(@TypeOf(pokemon.*), "dvs")) {
349
+ try self.writer.writeAll(&.{
350
+ @intFromEnum(pokemon.species),
351
+ @intFromEnum(pokemon.dvs.gender),
352
+ pokemon.level,
353
+ });
354
+ } else {
355
+ try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
356
+ }
268
357
  try self.writer.writeIntNative(u16, pokemon.hp);
269
358
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
270
359
  try self.writer.writeAll(&.{pokemon.status});
@@ -585,6 +674,133 @@ pub fn Log(comptime Writer: type) type {
585
674
  });
586
675
  }
587
676
 
677
+ pub fn drag(self: Self, ident: ID, pokemon: anytype) Error!void {
678
+ return switchDrag(self, .Drag, ident, pokemon);
679
+ }
680
+
681
+ pub fn item(self: Self, target: ID, i: anytype, source: ID) Error!void {
682
+ if (!enabled) return;
683
+
684
+ try self.writer.writeAll(&.{
685
+ @intFromEnum(ArgType.Item),
686
+ @as(u8, @bitCast(target)),
687
+ @intFromEnum(i),
688
+ @as(u8, @bitCast(source)),
689
+ });
690
+ }
691
+
692
+ pub fn enditem(self: Self, ident: ID, i: anytype, reason: EndItem) Error!void {
693
+ if (!enabled) return;
694
+
695
+ try self.writer.writeAll(&.{
696
+ @intFromEnum(ArgType.EndItem),
697
+ @as(u8, @bitCast(ident)),
698
+ @intFromEnum(i),
699
+ @intFromEnum(reason),
700
+ });
701
+ }
702
+
703
+ pub fn cureteam(self: Self, source: ID) Error!void {
704
+ if (!enabled) return;
705
+
706
+ try self.writer.writeAll(&.{
707
+ @intFromEnum(ArgType.CureTeam),
708
+ @as(u8, @bitCast(source)),
709
+ });
710
+ }
711
+
712
+ pub fn sethp(self: Self, ident: ID, pokemon: anytype, reason: SetHP) Error!void {
713
+ if (!enabled) return;
714
+
715
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(ident)) });
716
+ try self.writer.writeIntNative(u16, pokemon.hp);
717
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
718
+ try self.writer.writeAll(&.{pokemon.status});
719
+ try self.writer.writeByte(@intFromEnum(reason));
720
+ }
721
+
722
+ pub fn setboost(self: Self, ident: ID, num: i8) Error!void {
723
+ if (!enabled) return;
724
+
725
+ try self.writer.writeAll(&.{
726
+ @intFromEnum(ArgType.SetBoost),
727
+ @as(u8, @bitCast(ident)),
728
+ @as(u8, @intCast(num + 6)),
729
+ });
730
+ }
731
+
732
+ pub fn copyboost(self: Self, source: ID, target: ID) Error!void {
733
+ if (!enabled) return;
734
+
735
+ try self.writer.writeAll(&.{
736
+ @intFromEnum(ArgType.CopyBoost),
737
+ @as(u8, @bitCast(source)),
738
+ @as(u8, @bitCast(target)),
739
+ });
740
+ }
741
+
742
+ pub fn sidestart(self: Self, player: Player, reason: Side) Error!void {
743
+ if (!enabled) return;
744
+
745
+ try self.writer.writeAll(&.{
746
+ @intFromEnum(ArgType.SideStart),
747
+ @intFromEnum(player),
748
+ @intFromEnum(reason),
749
+ });
750
+ }
751
+
752
+ pub fn sideend(self: Self, player: Player, reason: Side) Error!void {
753
+ if (!enabled) return;
754
+
755
+ assert(@intFromEnum(reason) != @intFromEnum(Side.Spikes));
756
+ try self.writer.writeAll(&.{
757
+ @intFromEnum(ArgType.SideEnd),
758
+ @intFromEnum(player),
759
+ @intFromEnum(reason),
760
+ });
761
+ }
762
+
763
+ pub fn spikesend(self: Self, player: Player, m: anytype, source: ID) Error!void {
764
+ if (!enabled) return;
765
+
766
+ try self.writer.writeAll(&.{
767
+ @intFromEnum(ArgType.SideEnd),
768
+ @intFromEnum(player),
769
+ @intFromEnum(Side.Spikes),
770
+ @intFromEnum(m),
771
+ @as(u8, @bitCast(source)),
772
+ });
773
+ }
774
+
775
+ pub fn singlemove(self: Self, ident: ID, m: anytype) Error!void {
776
+ return single(self, .SingleMove, ident, m);
777
+ }
778
+
779
+ pub fn singleturn(self: Self, ident: ID, m: anytype) Error!void {
780
+ return single(self, .SingleTurn, ident, m);
781
+ }
782
+
783
+ fn single(self: Self, arg: ArgType, ident: ID, m: anytype) Error!void {
784
+ if (!enabled) return;
785
+
786
+ try self.writer.writeAll(&.{
787
+ @intFromEnum(arg),
788
+ @as(u8, @bitCast(ident)),
789
+ @intFromEnum(m),
790
+ });
791
+ }
792
+
793
+ pub fn weather(self: Self, w: anytype, reason: Weather) Error!void {
794
+ if (!enabled) return;
795
+
796
+ assert(!(w == .None and reason == .Upkeep));
797
+ try self.writer.writeAll(&.{
798
+ @intFromEnum(ArgType.Weather),
799
+ @intFromEnum(w),
800
+ @intFromEnum(reason),
801
+ });
802
+ }
803
+
588
804
  pub fn laststill(self: Self) Error!void {
589
805
  if (!enabled) return;
590
806
 
@@ -645,10 +861,14 @@ pub const ByteStream = struct {
645
861
  }
646
862
  };
647
863
 
648
- pub const Kind = enum { Move, Species, Type, Status };
649
- pub const Formatter = fn (Kind, u8) []const u8;
864
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
650
865
 
651
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
866
+ pub fn format(
867
+ comptime gen: anytype,
868
+ a: []const u8,
869
+ b: ?[]const u8,
870
+ color: bool,
871
+ ) void {
652
872
  print("\n", .{});
653
873
 
654
874
  var i: usize = 0;
@@ -686,6 +906,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
686
906
  .Resisted => "|-resisted|",
687
907
  .Immune => "|-immune|",
688
908
  .Transform => "|-transform|",
909
+ .Drag => "|drag|",
910
+ .Item => "|-item|",
911
+ .EndItem => "|-enditem|",
912
+ .CureTeam => "|-cureteam|",
913
+ .SetHP => "|-sethp|",
914
+ .SetBoost => "|-setboost|",
915
+ .CopyBoost => "|-copyboost|",
916
+ .SideStart => "|-sidestart|",
917
+ .SideEnd => "|-sideend|",
918
+ .SingleMove => "|-singlemove|",
919
+ .SingleTurn => "|-singleturn|",
920
+ .Weather => "|-weather|",
689
921
  else => unreachable,
690
922
  };
691
923
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -701,17 +933,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
701
933
  .Move => {
702
934
  const source = ID.from(@intCast(a[i]));
703
935
  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);
936
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
705
937
  const target = ID.from(@intCast(a[i]));
706
938
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
707
939
  const reason: Move = @enumFromInt(a[i]);
708
940
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
709
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
941
+ if (reason == .From) {
942
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
943
+ }
710
944
  },
711
- .Switch => {
945
+ .Switch, .Drag => {
712
946
  const id = ID.from(@intCast(a[i]));
713
947
  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);
948
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
949
+ if (@hasDecl(gen, "Gender")) {
950
+ if (a[i] == N(gen.Gender.Unknown)) {
951
+ i += 1;
952
+ } else {
953
+ var gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
954
+ printc(", {s}", .{gender}, a, b, &i, 1, color);
955
+ }
956
+ }
715
957
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
716
958
  switch (endian) {
717
959
  .Big => {
@@ -727,7 +969,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
727
969
  printc("/{d}", .{hp}, a, b, &i, 2, color);
728
970
  },
729
971
  }
730
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
972
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
731
973
  },
732
974
  .Cant => {
733
975
  const id = ID.from(@intCast(a[i]));
@@ -735,7 +977,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
735
977
  const reason: Cant = @enumFromInt(a[i]);
736
978
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
737
979
  if (reason == .Disable) {
738
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
980
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
739
981
  }
740
982
  },
741
983
  .Faint,
@@ -744,6 +986,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
744
986
  .Crit,
745
987
  .SuperEffective,
746
988
  .Resisted,
989
+ .CureTeam,
747
990
  => {
748
991
  const id = ID.from(@intCast(a[i]));
749
992
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
@@ -775,7 +1018,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
775
1018
  printc("/{d}", .{hp}, a, b, &i, 2, color);
776
1019
  },
777
1020
  }
778
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1021
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
779
1022
  if (arg == .Damage) {
780
1023
  const reason: Damage = @enumFromInt(a[i]);
781
1024
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -795,15 +1038,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
795
1038
  .Status => {
796
1039
  const id = ID.from(@intCast(a[i]));
797
1040
  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);
1041
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
799
1042
  const reason: Status = @enumFromInt(a[i]);
800
1043
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
801
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1044
+ if (reason == .From) {
1045
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1046
+ }
802
1047
  },
803
1048
  .CureStatus => {
804
1049
  const id = ID.from(@intCast(a[i]));
805
1050
  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);
1051
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
807
1052
  const reason: CureStatus = @enumFromInt(a[i]);
808
1053
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
809
1054
  },
@@ -818,15 +1063,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
818
1063
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
819
1064
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
820
1065
  },
821
- .HitCount => {
1066
+ .HitCount, .SetBoost => {
822
1067
  const id = ID.from(@intCast(a[i]));
823
1068
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
824
1069
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
825
1070
  },
826
- .Prepare => {
1071
+ .Prepare, .SingleMove, .SingleTurn => {
827
1072
  const id = ID.from(@intCast(a[i]));
828
1073
  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);
1074
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
830
1075
  },
831
1076
  .Activate => {
832
1077
  const id = ID.from(@intCast(a[i]));
@@ -841,12 +1086,12 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
841
1086
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
842
1087
  const types = @as(gen1.Types, @bitCast(a[i]));
843
1088
  const args = .{
844
- formatter(.Type, @intFromEnum(types.type1)),
845
- formatter(.Type, @intFromEnum(types.type2)),
1089
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1090
+ formatter(gen, .Type, @intFromEnum(types.type2)),
846
1091
  };
847
1092
  printc(" {s}/{s}", args, a, b, &i, 1, color);
848
1093
  } else if (reason >= @intFromEnum(Start.Disable)) {
849
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1094
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
850
1095
  }
851
1096
  },
852
1097
  .End => {
@@ -859,12 +1104,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
859
1104
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
860
1105
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
861
1106
  },
862
- .Transform => {
1107
+ .Transform, .CopyBoost => {
863
1108
  const source = ID.from(@intCast(a[i]));
864
1109
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
865
1110
  const target = ID.from(@intCast(a[i]));
866
1111
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
867
1112
  },
1113
+ .Item, .EndItem => {
1114
+ var id = ID.from(@intCast(a[i]));
1115
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1116
+ printc(" {s}", .{formatter(gen, .Item, a[i])}, a, b, &i, 1, color);
1117
+ if (arg == .Item) {
1118
+ id = ID.from(@intCast(a[i]));
1119
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1120
+ } else {
1121
+ const reason: EndItem = @enumFromInt(a[i]);
1122
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1123
+ }
1124
+ },
1125
+ .SetHP => {
1126
+ const id = ID.from(@intCast(a[i]));
1127
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1128
+ switch (endian) {
1129
+ .Big => {
1130
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1131
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1132
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1133
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1134
+ },
1135
+ .Little => {
1136
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1137
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1138
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1139
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1140
+ },
1141
+ }
1142
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1143
+ printc(" {s}", .{@tagName(@as(SetHP, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1144
+ },
1145
+ .SideStart => {
1146
+ const id = ID.from(@intCast(a[i]));
1147
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1148
+ printc(" {s}", .{@tagName(@as(Side, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1149
+ },
1150
+ .SideEnd => {
1151
+ var id = ID.from(@intCast(a[i]));
1152
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1153
+ const reason: Side = @enumFromInt(a[i]);
1154
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1155
+ if (reason == .Spikes) {
1156
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1157
+ id = ID.from(@intCast(a[i]));
1158
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1159
+ }
1160
+ },
1161
+ .Weather => {
1162
+ printc(" {s}", .{formatter(gen, .Weather, a[i])}, a, b, &i, 1, color);
1163
+ printc(" {s}", .{@tagName(@as(Weather, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1164
+ },
868
1165
  else => unreachable,
869
1166
  }
870
1167
  print("\n", .{});
@@ -896,7 +1193,7 @@ fn printc(
896
1193
  }
897
1194
 
898
1195
  pub fn expectLog(
899
- comptime formatter: Formatter,
1196
+ comptime gen: anytype,
900
1197
  expected: []const u8,
901
1198
  actual: []const u8,
902
1199
  offset: usize,
@@ -915,8 +1212,8 @@ pub fn expectLog(
915
1212
 
916
1213
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
917
1214
  error.TestExpectedEqual => {
918
- format(formatter, expected, null, color);
919
- format(formatter, actual, expected, color);
1215
+ format(gen, expected, null, color);
1216
+ format(gen, actual, expected, color);
920
1217
  return err;
921
1218
  },
922
1219
  else => return err,
@@ -956,49 +1253,70 @@ const gen1 = struct {
956
1253
  pub const helpers = @import("../gen1/helpers.zig");
957
1254
  };
958
1255
 
1256
+ const gen2 = struct {
1257
+ pub usingnamespace @import("../gen2/data.zig");
1258
+ pub const helpers = @import("../gen2/helpers.zig");
1259
+ };
1260
+
959
1261
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
960
- var stream = ByteStream{ .buffer = &buf };
1262
+ var stream: ByteStream = .{ .buffer = &buf };
961
1263
  var log: FixedLog = .{ .writer = stream.writer() };
962
1264
 
963
- const M = gen1.Move;
964
- const S = gen1.Species;
1265
+ const M1 = gen1.Move;
1266
+ const S1 = gen1.Species;
1267
+
1268
+ const M2 = gen2.Move;
1269
+ const S2 = gen2.Species;
1270
+ const I2 = gen2.Item;
1271
+ const W2 = gen2.Weather;
965
1272
 
966
1273
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
967
- return expectLog(gen1Formatter, expected, actual, 0);
1274
+ return expectLog(gen1, expected, actual, 0);
1275
+ }
1276
+
1277
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1278
+ return expectLog(gen2, expected, actual, 0);
968
1279
  }
969
1280
 
970
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1281
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
971
1282
  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),
1283
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1284
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1285
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1286
+ .Status => gen.Status.name(byte),
1287
+ .Item => if (@hasDecl(gen, "Item"))
1288
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1289
+ else
1290
+ unreachable,
1291
+ .Weather => if (@hasDecl(gen, "Weather"))
1292
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1293
+ else
1294
+ unreachable,
976
1295
  };
977
1296
  }
978
1297
 
979
1298
  test "|move|" {
980
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1299
+ try log.move(p2.ident(4), M1.Thunderbolt, p1.ident(5));
981
1300
  try expectLog1(
982
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1301
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
983
1302
  buf[0..5],
984
1303
  );
985
1304
  stream.reset();
986
1305
 
987
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
988
- const wrap = N(M.Wrap);
1306
+ try log.moveFrom(p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome);
989
1307
  try expectLog1(
990
- &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
1308
+ &.{ N(ArgType.Move), 0b1100, N(M1.Pound), 0b0101, N(Move.From), N(M1.Metronome) },
991
1309
  buf[0..6],
992
1310
  );
993
1311
  stream.reset();
994
1312
 
995
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1313
+ try log.move(p2.ident(4), M1.SkullBash, .{});
996
1314
  try log.laststill();
997
1315
  try expectLog1(
998
1316
  &.{
999
1317
  N(ArgType.Move),
1000
1318
  0b1100,
1001
- N(M.SkullBash),
1319
+ N(M1.SkullBash),
1002
1320
  0,
1003
1321
  N(Move.None),
1004
1322
  N(ArgType.LastStill),
@@ -1007,13 +1325,13 @@ test "|move|" {
1007
1325
  );
1008
1326
  stream.reset();
1009
1327
 
1010
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1328
+ try log.move(p2.ident(4), M1.Tackle, p1.ident(5));
1011
1329
  try log.lastmiss();
1012
1330
  try expectLog1(
1013
1331
  &.{
1014
1332
  N(ArgType.Move),
1015
1333
  0b1100,
1016
- N(M.Tackle),
1334
+ N(M1.Tackle),
1017
1335
  0b0101,
1018
1336
  N(Move.None),
1019
1337
  N(ArgType.LastMiss),
@@ -1032,8 +1350,8 @@ test "|switch|" {
1032
1350
  try log.switched(p2.ident(3), &snorlax);
1033
1351
  const par = 0b1000000;
1034
1352
  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 },
1353
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1354
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1037
1355
  };
1038
1356
  try expectLog1(expected, buf[0..9]);
1039
1357
  stream.reset();
@@ -1043,8 +1361,8 @@ test "|switch|" {
1043
1361
  snorlax.status = 0;
1044
1362
  try log.switched(p2.ident(3), &snorlax);
1045
1363
  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 },
1364
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1365
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1048
1366
  };
1049
1367
  try expectLog1(expected, buf[0..9]);
1050
1368
  stream.reset();
@@ -1052,11 +1370,30 @@ test "|switch|" {
1052
1370
  snorlax.hp = 400;
1053
1371
  try log.switched(p2.ident(3), &snorlax);
1054
1372
  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 },
1373
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1374
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1057
1375
  };
1058
1376
  try expectLog1(expected, buf[0..9]);
1059
1377
  stream.reset();
1378
+
1379
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1380
+ blissey.level = 91;
1381
+ blissey.hp = 200;
1382
+ blissey.stats.hp = 400;
1383
+ blissey.status = gen2.Status.init(.PAR);
1384
+ try log.switched(p2.ident(3), &blissey);
1385
+ expected = &(.{
1386
+ N(ArgType.Switch),
1387
+ 0b1011,
1388
+ N(S2.Blissey),
1389
+ N(gen2.Gender.Female),
1390
+ 91,
1391
+ } ++ switch (endian) {
1392
+ .Big => .{ 0, 200, 1, 144, par },
1393
+ .Little => .{ 200, 0, 144, 1, par },
1394
+ });
1395
+ try expectLog2(expected, buf[0..10]);
1396
+ stream.reset();
1060
1397
  }
1061
1398
 
1062
1399
  test "|cant|" {
@@ -1064,8 +1401,8 @@ test "|cant|" {
1064
1401
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1065
1402
  stream.reset();
1066
1403
 
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]);
1404
+ try log.disabled(p1.ident(2), M1.Earthquake);
1405
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1069
1406
  stream.reset();
1070
1407
  }
1071
1408
 
@@ -1175,9 +1512,9 @@ test "|-status|" {
1175
1512
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1176
1513
  stream.reset();
1177
1514
 
1178
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1515
+ try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M1.BodySlam);
1179
1516
  try expectLog1(
1180
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1517
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1181
1518
  buf[0..5],
1182
1519
  );
1183
1520
  stream.reset();
@@ -1248,8 +1585,8 @@ test "|-hitcount|" {
1248
1585
  }
1249
1586
 
1250
1587
  test "|-prepare|" {
1251
- try log.prepare(p2.ident(2), M.Dig);
1252
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1588
+ try log.prepare(p2.ident(2), M1.Dig);
1589
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1253
1590
  stream.reset();
1254
1591
  }
1255
1592
 
@@ -1306,12 +1643,12 @@ test "|-start|" {
1306
1643
  );
1307
1644
  stream.reset();
1308
1645
 
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]);
1646
+ try log.startEffect(p1.ident(2), .Disable, M1.Surf);
1647
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1311
1648
  stream.reset();
1312
1649
 
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]);
1650
+ try log.startEffect(p1.ident(2), .Mimic, M1.Surf);
1651
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1315
1652
  stream.reset();
1316
1653
  }
1317
1654
 
@@ -1330,21 +1667,25 @@ test "|-ohko|" {
1330
1667
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1331
1668
  stream.reset();
1332
1669
  }
1670
+
1333
1671
  test "|-crit|" {
1334
1672
  try log.crit(p2.ident(5));
1335
1673
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1336
1674
  stream.reset();
1337
1675
  }
1676
+
1338
1677
  test "|-supereffective|" {
1339
1678
  try log.supereffective(p1.ident(1));
1340
1679
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1341
1680
  stream.reset();
1342
1681
  }
1682
+
1343
1683
  test "|-resisted|" {
1344
1684
  try log.resisted(p2.ident(2));
1345
1685
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1346
1686
  stream.reset();
1347
1687
  }
1688
+
1348
1689
  test "|-immune|" {
1349
1690
  try log.immune(p1.ident(3), .None);
1350
1691
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
@@ -1354,8 +1695,125 @@ test "|-immune|" {
1354
1695
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1355
1696
  stream.reset();
1356
1697
  }
1698
+
1357
1699
  test "|-transform|" {
1358
1700
  try log.transform(p2.ident(4), p1.ident(5));
1359
1701
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1360
1702
  stream.reset();
1361
1703
  }
1704
+
1705
+ test "|drag|" {
1706
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1707
+ blissey.level = 91;
1708
+ blissey.hp = 200;
1709
+ blissey.stats.hp = 400;
1710
+ blissey.status = gen2.Status.init(.PAR);
1711
+ try log.drag(p2.ident(3), &blissey);
1712
+ const par = 0b1000000;
1713
+ const expected = &(.{
1714
+ N(ArgType.Drag),
1715
+ 0b1011,
1716
+ N(S2.Blissey),
1717
+ N(gen2.Gender.Female),
1718
+ 91,
1719
+ } ++ switch (endian) {
1720
+ .Big => .{ 0, 200, 1, 144, par },
1721
+ .Little => .{ 200, 0, 144, 1, par },
1722
+ });
1723
+ try expectLog2(expected, buf[0..10]);
1724
+ stream.reset();
1725
+ }
1726
+
1727
+ test "|-item|" {
1728
+ try log.item(p2.ident(2), I2.GoldBerry, p1.ident(4));
1729
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1730
+ stream.reset();
1731
+ }
1732
+
1733
+ test "|-enditem|" {
1734
+ try log.enditem(p1.ident(1), I2.BerserkGene, .None);
1735
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1736
+ stream.reset();
1737
+
1738
+ try log.enditem(p2.ident(4), I2.PRZCureBerry, .Eat);
1739
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1740
+ stream.reset();
1741
+ }
1742
+
1743
+ test "|-cureteam|" {
1744
+ try log.cureteam(p2.ident(5));
1745
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1746
+ stream.reset();
1747
+ }
1748
+
1749
+ test "|-sethp|" {
1750
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1751
+ blissey.hp = 612;
1752
+ try log.sethp(p2.ident(2), &blissey, .Silent);
1753
+ var expected: []const u8 = switch (endian) {
1754
+ .Big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1755
+ .Little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1756
+ };
1757
+ try expectLog2(expected, buf[0..8]);
1758
+ stream.reset();
1759
+ }
1760
+
1761
+ test "|-setboost|" {
1762
+ try log.setboost(p2.ident(6), 6);
1763
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1764
+ stream.reset();
1765
+
1766
+ try log.setboost(p2.ident(3), 2);
1767
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1768
+ stream.reset();
1769
+ }
1770
+
1771
+ test "|-copyboost|" {
1772
+ try log.copyboost(p2.ident(3), p1.ident(6));
1773
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1774
+ stream.reset();
1775
+ }
1776
+
1777
+ test "|-sidestart|" {
1778
+ try log.sidestart(.P2, .Reflect);
1779
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1780
+ stream.reset();
1781
+ }
1782
+
1783
+ test "|-sideend|" {
1784
+ try log.sideend(.P2, .LightScreen);
1785
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1786
+ stream.reset();
1787
+
1788
+ try log.spikesend(.P1, M2.RapidSpin, p1.ident(3));
1789
+ try expectLog2(&.{
1790
+ N(ArgType.SideEnd),
1791
+ 0,
1792
+ N(Side.Spikes),
1793
+ N(M2.RapidSpin),
1794
+ 0b0011,
1795
+ }, buf[0..5]);
1796
+ stream.reset();
1797
+ }
1798
+
1799
+ test "|-singlemove|" {
1800
+ try log.singlemove(p2.ident(2), M2.DestinyBond);
1801
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1802
+ stream.reset();
1803
+ }
1804
+
1805
+ test "|-singleturn|" {
1806
+ try log.singleturn(p1.ident(3), M2.Protect);
1807
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1808
+ stream.reset();
1809
+ }
1810
+
1811
+ test "|-weather|" {
1812
+ try log.weather(W2.Rain, .Upkeep);
1813
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1814
+ stream.reset();
1815
+
1816
+ try log.weather(W2.None, .None);
1817
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1818
+ stream.reset();
1819
+ }