@pkmn/engine 0.1.0-dev.5ea92492 → 0.1.0-dev.5fbc7aef

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +141 -76
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +28 -6
  13. package/build/pkg/addon.js +42 -73
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/ids.json +12 -12
  16. package/build/pkg/data/layout.json +40 -5
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +18 -20
  21. package/build/pkg/gen1.js +76 -51
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +57 -103
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +24 -3
  27. package/build/pkg/protocol.js +127 -104
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +69 -74
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +152 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +359 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +33 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +336 -166
  40. package/package.json +71 -120
  41. package/src/bin/install-pkmn-engine +57 -26
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/bindings/node.zig +117 -0
  45. package/src/lib/bindings/wasm.zig +50 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/battle.zig +17 -0
  48. package/src/lib/common/data.zig +19 -31
  49. package/src/lib/common/debug.zig +51 -20
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +61 -0
  52. package/src/lib/common/options.zig +18 -3
  53. package/src/lib/common/protocol.zig +1050 -497
  54. package/src/lib/common/rational.zig +440 -0
  55. package/src/lib/common/rng.zig +31 -24
  56. package/src/lib/common/util.zig +44 -0
  57. package/src/lib/gen1/calc.zig +956 -0
  58. package/src/lib/gen1/chance.zig +1170 -0
  59. package/src/lib/gen1/data/moves.zig +203 -193
  60. package/src/lib/gen1/data/species.zig +6 -8
  61. package/src/lib/gen1/data/types.zig +12 -13
  62. package/src/lib/gen1/data.zig +104 -94
  63. package/src/lib/gen1/helpers.zig +138 -51
  64. package/src/lib/gen1/mechanics.zig +2720 -2351
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +896 -0
  67. package/src/lib/gen2/data/items.zig +15 -23
  68. package/src/lib/gen2/data/moves.zig +665 -573
  69. package/src/lib/gen2/data/species.zig +3 -5
  70. package/src/lib/gen2/data/types.zig +141 -36
  71. package/src/lib/gen2/data.zig +140 -104
  72. package/src/lib/gen2/generated.zig +801 -0
  73. package/src/lib/gen2/helpers.zig +58 -45
  74. package/src/lib/gen2/mechanics.zig +3582 -30
  75. package/src/lib/napi.h +2 -0
  76. package/src/lib/node.zig +14 -0
  77. package/src/lib/pkmn.zig +177 -22
  78. package/src/lib/wasm.zig +13 -0
  79. package/src/tools/display/dom.ts +93 -0
  80. package/src/tools/display/ui.css +354 -0
  81. package/build/test/display.js +0 -382
  82. package/build/test/display.js.map +0 -1
  83. package/src/lib/binding/node.zig +0 -229
  84. package/src/lib/binding/wasm.zig +0 -10
  85. package/src/lib/gen1/README.md +0 -791
  86. package/src/lib/gen2/README.md +0 -309
  87. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,16 +1,17 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
4
- const data = @import("./data.zig");
5
- const options = @import("./options.zig");
2
+ const data = @import("data.zig");
3
+ const options = @import("options.zig");
4
+ const pkmn = @import("../pkmn.zig");
5
+ const std = @import("std");
6
6
 
7
7
  const assert = std.debug.assert;
8
+ const enabled = options.log;
9
+ const ID = data.ID;
10
+ const Player = data.Player;
8
11
  const print = std.debug.print;
9
12
 
10
- const trace = options.trace;
11
-
12
- const Player = data.Player;
13
- const ID = data.ID;
13
+ const endian = builtin.cpu.arch.endian();
14
+ const Endian = std.builtin.Endian;
14
15
 
15
16
  pub const ArgType = enum(u8) {
16
17
  None,
@@ -69,7 +70,7 @@ pub const ArgType = enum(u8) {
69
70
  EndAbility,
70
71
  ClearNegativeBoost,
71
72
  FormeChange,
72
- NoTarget,
73
+ LastNoTarget, // FIXME
73
74
 
74
75
  // Gen 4
75
76
  SwapBoost,
@@ -102,6 +103,10 @@ pub const ArgType = enum(u8) {
102
103
  // Gen 8
103
104
  CanDynamax,
104
105
  SwapSideConditions,
106
+ UpdatePoke,
107
+
108
+ // Gen 9
109
+ Terastallize,
105
110
  };
106
111
 
107
112
  pub const Move = enum(u8) {
@@ -118,12 +123,19 @@ pub const Cant = enum(u8) {
118
123
  Disable,
119
124
  Recharge,
120
125
  PP,
126
+
127
+ Attract,
121
128
  };
122
129
 
123
130
  pub const Heal = enum(u8) {
124
131
  None,
125
132
  Silent,
126
133
  Drain,
134
+
135
+ Leftovers,
136
+ Berry,
137
+ BerryJuice,
138
+ GoldBerry,
127
139
  };
128
140
 
129
141
  pub const Damage = enum(u8) {
@@ -133,6 +145,17 @@ pub const Damage = enum(u8) {
133
145
  Confusion,
134
146
  LeechSeed,
135
147
  RecoilOf,
148
+
149
+ Bind,
150
+ Wrap,
151
+ FireSpin,
152
+ Clamp,
153
+ Whirlpool,
154
+ Spikes,
155
+
156
+ Nightmare,
157
+ Curse,
158
+ Sandstorm,
136
159
  };
137
160
 
138
161
  pub const Status = enum(u8) {
@@ -175,8 +198,26 @@ pub const Activate = enum(u8) {
175
198
  Haze,
176
199
  Mist,
177
200
  Struggle,
178
- Substitute,
201
+ Substitute, // FIXME SubstituteDamage
179
202
  Splash,
203
+
204
+ Rage, // TODO
205
+
206
+ Attract,
207
+ LockOn,
208
+ Bind,
209
+ Wrap,
210
+ FireSpin,
211
+ Clamp,
212
+ Whirlpool,
213
+ SubstituteBlock,
214
+
215
+ DestinyBond,
216
+
217
+ BeatUp, // FIXME of
218
+
219
+ Magnitude,
220
+ Spite,
180
221
  };
181
222
 
182
223
  pub const Start = enum(u8) {
@@ -194,6 +235,19 @@ pub const Start = enum(u8) {
194
235
 
195
236
  Disable,
196
237
  Mimic,
238
+
239
+ Attract,
240
+ Nightmare,
241
+ Curse,
242
+ Foresight,
243
+ Encore,
244
+ FutureSight,
245
+
246
+ PerishSong0,
247
+ PerishSong1,
248
+ PerishSong2,
249
+ PerishSong3,
250
+ PerishSong3Silent,
197
251
  };
198
252
 
199
253
  pub const End = enum(u8) {
@@ -202,16 +256,30 @@ pub const End = enum(u8) {
202
256
  Bide,
203
257
  Substitute,
204
258
 
205
- // Silent
259
+ Nightmare,
260
+ Curse,
261
+ Foresight,
262
+ Encore,
263
+ FutureSight,
264
+ LeechSeed,
265
+
266
+ Bind,
267
+ Wrap,
268
+ FireSpin,
269
+ Clamp,
270
+ Whirlpool,
271
+
206
272
  DisableSilent,
207
273
  ConfusionSilent,
208
-
209
- Mist,
210
- FocusEnergy,
211
- LeechSeed,
212
- Toxic,
213
- LightScreen,
214
- Reflect,
274
+ MistSilent,
275
+ FocusEnergySilent,
276
+ LeechSeedSilent,
277
+ ToxicSilent,
278
+ LightScreenSilent,
279
+ ReflectSilent,
280
+ BideSilent,
281
+
282
+ LeechSeedFrom,
215
283
  };
216
284
 
217
285
  pub const Immune = enum(u8) {
@@ -219,381 +287,612 @@ pub const Immune = enum(u8) {
219
287
  OHKO,
220
288
  };
221
289
 
222
- pub const NULL = Log(@TypeOf(std.io.null_writer)){ .writer = std.io.null_writer };
290
+ pub const EndItem = enum(u8) {
291
+ None,
292
+ Eat,
293
+ };
294
+
295
+ pub const SetHP = enum(u8) {
296
+ None,
297
+ Silent,
298
+ };
299
+
300
+ pub const Side = enum(u8) {
301
+ Safeguard,
302
+ Reflect,
303
+ LightScreen,
304
+ Spikes,
305
+ };
306
+
307
+ pub const Weather = enum(u8) {
308
+ None,
309
+ Upkeep,
310
+ };
223
311
 
224
- pub fn Log(comptime Writer: type) type {
312
+ /// Logs protocol information to its `WriterT` during a battle update when `options.log` is enabled.
313
+ pub fn Log(comptime WriterT: type) type {
225
314
  return struct {
226
315
  const Self = @This();
227
316
 
228
- writer: Writer,
317
+ writer: WriterT,
229
318
 
230
- pub const Error = Writer.Error;
319
+ pub const Error = err(WriterT);
231
320
 
232
- pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
233
- if (!trace) return;
234
- assert(m != .None);
321
+ fn err(comptime T: type) type {
322
+ const msg = "Invalid Writer type " ++ @typeName(T) ++ "'";
323
+ return switch (@typeInfo(T)) {
324
+ .pointer => |ptr_info| switch (ptr_info.size) {
325
+ .one => switch (@typeInfo(ptr_info.child)) {
326
+ .@"struct" => err(ptr_info.child),
327
+ else => @compileError(msg),
328
+ },
329
+ else => @compileError(msg),
330
+ },
331
+ .@"struct" => if (@hasDecl(T, "Error")) T.Error else @compileError(msg),
332
+ else => @compileError(msg),
333
+ };
334
+ }
335
+
336
+ // source: ID, move: <Move>, target: ID, from?: <Move>
337
+ pub fn move(self: Self, args: anytype) Error!void {
338
+ if (!enabled) return;
339
+
340
+ assert(args[1] != .None);
235
341
  try self.writer.writeAll(&.{
236
- @enumToInt(ArgType.Move),
237
- @bitCast(u8, source),
238
- @enumToInt(m),
239
- @bitCast(u8, target),
342
+ @intFromEnum(ArgType.Move),
343
+ @as(u8, @bitCast(args[0])),
344
+ @intFromEnum(args[1]),
345
+ @as(u8, @bitCast(args[2])),
240
346
  });
241
- const none = &[_]u8{@enumToInt(Move.None)};
242
- try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
243
- .Null => none,
244
- .Optional => if (from) |f| blk: {
245
- assert(@enumToInt(f) != 0);
246
- break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(f) };
247
- } else none,
248
- else => blk: {
249
- assert(@enumToInt(from) != 0);
250
- break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(from) };
251
- },
347
+ try self.writer.writeAll(if (args.len < 4 or @intFromEnum(args[3]) == 0)
348
+ &.{@intFromEnum(Move.None)}
349
+ else
350
+ &.{ @intFromEnum(Move.From), @intFromEnum(args[3]) });
351
+ }
352
+
353
+ // ident: ID, pokemon: <Pokemon>
354
+ pub fn switched(self: Self, args: anytype) Error!void {
355
+ return switchDrag(self, .Switch, args);
356
+ }
357
+
358
+ // ident: ID, pokemon: <Pokemon>
359
+ fn switchDrag(self: Self, arg: ArgType, args: anytype) Error!void {
360
+ if (!enabled) return;
361
+
362
+ try self.writer.writeAll(&.{ @intFromEnum(arg), @as(u8, @bitCast(args[0])) });
363
+ if (@hasField(@TypeOf(args[1].*), "dvs")) {
364
+ try self.writer.writeAll(&.{
365
+ @intFromEnum(args[1].species),
366
+ @intFromEnum(args[1].dvs.gender),
367
+ args[1].level,
368
+ });
369
+ } else {
370
+ try self.writer.writeAll(&.{ @intFromEnum(args[1].species), args[1].level });
371
+ }
372
+ try self.writer.writeInt(u16, args[1].hp, endian);
373
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
374
+ try self.writer.writeAll(&.{args[1].status});
375
+ }
376
+
377
+ // ident: ID, reason: Cant
378
+ // ident: ID, reason: .Disable, move: <Move>
379
+ pub fn cant(self: Self, args: anytype) Error!void {
380
+ if (!enabled) return;
381
+
382
+ assert(args[1] != .Disable or args.len == 3);
383
+ try self.writer.writeAll(&.{
384
+ @intFromEnum(ArgType.Cant),
385
+ @as(u8, @bitCast(args[0])),
386
+ @intFromEnum(@as(Cant, args[1])),
252
387
  });
388
+ if (args.len == 3) try self.writer.writeByte(@intFromEnum(args[2]));
253
389
  }
254
390
 
255
- pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
256
- if (!trace) return;
257
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Switch), @bitCast(u8, ident) });
258
- try self.writer.writeAll(&.{ @enumToInt(pokemon.species), pokemon.level });
259
- try self.writer.writeIntNative(u16, pokemon.hp);
260
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
261
- try self.writer.writeAll(&.{pokemon.status});
391
+ // ident: ID, done: bool
392
+ pub fn faint(self: Self, args: anytype) Error!void {
393
+ if (!enabled) return;
394
+
395
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(args[0])) });
396
+ if (args[1]) try self.writer.writeByte(@intFromEnum(ArgType.None));
397
+ }
398
+
399
+ // num: u16
400
+ pub fn turn(self: Self, args: anytype) Error!void {
401
+ if (!enabled) return;
402
+
403
+ try self.writer.writeByte(@intFromEnum(ArgType.Turn));
404
+ try self.writer.writeInt(u16, args[0], endian);
405
+ try self.writer.writeByte(@intFromEnum(ArgType.None));
262
406
  }
263
407
 
264
- pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
265
- if (!trace) return;
266
- assert(reason != .Disable);
408
+ // player: Player
409
+ pub fn win(self: Self, args: anytype) Error!void {
410
+ if (!enabled) return;
411
+
267
412
  try self.writer.writeAll(&.{
268
- @enumToInt(ArgType.Cant),
269
- @bitCast(u8, ident),
270
- @enumToInt(reason),
413
+ @intFromEnum(ArgType.Win),
414
+ @intFromEnum(@as(Player, args[0])),
415
+ @intFromEnum(ArgType.None),
271
416
  });
272
417
  }
273
418
 
274
- pub fn disabled(self: Self, ident: ID, m: anytype) Error!void {
275
- if (!trace) return;
419
+ // -
420
+ pub fn tie(self: Self, _: anytype) Error!void {
421
+ if (!enabled) return;
422
+
423
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Tie), @intFromEnum(ArgType.None) });
424
+ }
425
+
426
+ // ident: ID, pokemon: <Pokemon>, reason: Damage
427
+ // ident: ID, pokemon: <Pokemon>, reason: .RecoilOf, source: ID
428
+ pub fn damage(self: Self, args: anytype) Error!void {
429
+ if (!enabled) return;
430
+
431
+ assert(args[2] != .RecoilOf or args.len == 4);
276
432
  try self.writer.writeAll(&.{
277
- @enumToInt(ArgType.Cant),
278
- @bitCast(u8, ident),
279
- @enumToInt(Cant.Disable),
280
- @enumToInt(m),
433
+ @intFromEnum(ArgType.Damage),
434
+ @as(u8, @bitCast(args[0])),
281
435
  });
436
+ try self.writer.writeInt(u16, args[1].hp, endian);
437
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
438
+ try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Damage, args[2])) });
439
+ if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
282
440
  }
283
441
 
284
- pub fn faint(self: Self, ident: ID, done: bool) Error!void {
285
- if (!trace) return;
286
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Faint), @bitCast(u8, ident) });
287
- if (done) try self.writer.writeByte(@enumToInt(ArgType.None));
442
+ // ident: ID, pokemon: <Pokemon>, reason: Heal
443
+ // ident: ID, pokemon: <Pokemon>, reason: .Drain, target: ID
444
+ pub fn heal(self: Self, args: anytype) Error!void {
445
+ if (!enabled) return;
446
+
447
+ assert(args[2] != .Drain or args.len == 4);
448
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(args[0])) });
449
+ try self.writer.writeInt(u16, args[1].hp, endian);
450
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
451
+ try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Heal, args[2])) });
452
+ if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
288
453
  }
289
454
 
290
- pub fn turn(self: Self, num: u16) Error!void {
291
- if (!trace) return;
292
- try self.writer.writeByte(@enumToInt(ArgType.Turn));
293
- try self.writer.writeIntNative(u16, num);
294
- try self.writer.writeByte(@enumToInt(ArgType.None));
455
+ // ident: ID, value: u8, reason: Status
456
+ // ident: ID, value: u8, reason: .From, m: <Move>
457
+ pub fn status(self: Self, args: anytype) Error!void {
458
+ if (!enabled) return;
459
+
460
+ assert(args[2] != .From or args.len == 4);
461
+ try self.writer.writeAll(&.{
462
+ @intFromEnum(ArgType.Status),
463
+ @as(u8, @bitCast(args[0])),
464
+ args[1],
465
+ @intFromEnum(@as(Status, args[2])),
466
+ });
467
+ if (args.len == 4) try self.writer.writeByte(@intFromEnum(args[3]));
295
468
  }
296
469
 
297
- pub fn win(self: Self, player: Player) Error!void {
298
- if (!trace) return;
470
+ // ident: ID, value: u8, reason: CureStatus
471
+ pub fn curestatus(self: Self, args: anytype) Error!void {
472
+ if (!enabled) return;
473
+
299
474
  try self.writer.writeAll(&.{
300
- @enumToInt(ArgType.Win),
301
- @enumToInt(player),
302
- @enumToInt(ArgType.None),
475
+ @intFromEnum(ArgType.CureStatus),
476
+ @as(u8, @bitCast(args[0])),
477
+ args[1],
478
+ @intFromEnum(@as(CureStatus, args[2])),
303
479
  });
304
480
  }
305
481
 
306
- pub fn tie(self: Self) Error!void {
307
- if (!trace) return;
308
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Tie), @enumToInt(ArgType.None) });
482
+ // ident: ID, reason: Boost, num: i8
483
+ pub fn boost(self: Self, args: anytype) Error!void {
484
+ if (!enabled) return;
485
+
486
+ assert(args[2] != 0 and (args[1] != .Rage or args[2] > 0));
487
+ try self.writer.writeAll(&.{
488
+ @intFromEnum(ArgType.Boost),
489
+ @as(u8, @bitCast(args[0])),
490
+ @intFromEnum(@as(Boost, args[1])),
491
+ @as(u8, @intCast(@as(i8, args[2]) + 6)),
492
+ });
309
493
  }
310
494
 
311
- pub fn damage(self: Self, ident: ID, pokemon: anytype, reason: Damage) Error!void {
312
- if (!trace) return;
313
- assert(@enumToInt(reason) <= @enumToInt(Damage.LeechSeed));
314
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
315
- try self.writer.writeIntNative(u16, pokemon.hp);
316
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
317
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
495
+ // -
496
+ pub fn clearallboost(self: Self, _: anytype) Error!void {
497
+ if (!enabled) return;
498
+
499
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.ClearAllBoost)});
318
500
  }
319
501
 
320
- pub fn damageOf(
321
- self: Self,
322
- ident: ID,
323
- pokemon: anytype,
324
- reason: Damage,
325
- source: ID,
326
- ) Error!void {
327
- if (!trace) return;
328
- assert(@enumToInt(reason) == @enumToInt(Damage.RecoilOf));
329
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
330
- try self.writer.writeIntNative(u16, pokemon.hp);
331
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
502
+ // ident: ID, reason: Fail
503
+ pub fn fail(self: Self, args: anytype) Error!void {
504
+ if (!enabled) return;
505
+
332
506
  try self.writer.writeAll(&.{
333
- pokemon.status,
334
- @enumToInt(reason),
335
- @bitCast(u8, source),
507
+ @intFromEnum(ArgType.Fail),
508
+ @as(u8, @bitCast(args[0])),
509
+ @intFromEnum(@as(Fail, args[1])),
336
510
  });
337
511
  }
338
512
 
339
- pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) Error!void {
340
- if (!trace) return;
341
- assert(reason != .Drain);
342
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, ident) });
343
- try self.writer.writeIntNative(u16, pokemon.hp);
344
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
345
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
513
+ // source: ID
514
+ pub fn miss(self: Self, args: anytype) Error!void {
515
+ if (!enabled) return;
516
+
517
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(args[0])) });
346
518
  }
347
519
 
348
- pub fn drain(self: Self, source: ID, pokemon: anytype, target: ID) Error!void {
349
- if (!trace) return;
350
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, source) });
351
- try self.writer.writeIntNative(u16, pokemon.hp);
352
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
520
+ // ident: ID, num: u8
521
+ pub fn hitcount(self: Self, args: anytype) Error!void {
522
+ if (!enabled) return;
523
+
353
524
  try self.writer.writeAll(&.{
354
- pokemon.status,
355
- @enumToInt(Heal.Drain),
356
- @bitCast(u8, target),
525
+ @intFromEnum(ArgType.HitCount),
526
+ @as(u8, @bitCast(args[0])),
527
+ args[1],
357
528
  });
358
529
  }
359
530
 
360
- pub fn status(self: Self, ident: ID, value: u8, reason: Status) Error!void {
361
- if (!trace) return;
362
- assert(reason != .From);
531
+ // source: ID, m: <Move>
532
+ pub fn prepare(self: Self, args: anytype) Error!void {
533
+ if (!enabled) return;
534
+
363
535
  try self.writer.writeAll(&.{
364
- @enumToInt(ArgType.Status),
365
- @bitCast(u8, ident),
366
- value,
367
- @enumToInt(reason),
536
+ @intFromEnum(ArgType.Prepare),
537
+ @as(u8, @bitCast(args[0])),
538
+ @intFromEnum(args[1]),
368
539
  });
369
540
  }
370
541
 
371
- pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
372
- if (!trace) return;
542
+ // ident: ID
543
+ pub fn mustrecharge(self: Self, args: anytype) Error!void {
544
+ if (!enabled) return;
545
+
373
546
  try self.writer.writeAll(&.{
374
- @enumToInt(ArgType.Status),
375
- @bitCast(u8, ident),
376
- value,
377
- @enumToInt(Status.From),
378
- @enumToInt(m),
547
+ @intFromEnum(ArgType.MustRecharge),
548
+ @as(u8, @bitCast(args[0])),
379
549
  });
380
550
  }
381
551
 
382
- pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
383
- if (!trace) return;
552
+ // ident: ID, reason: Activate
553
+ // ident: ID, reason: .SubstituteBlock, m: <Move>
554
+ // ident: ID, reason: .Magnitude, num: u8
555
+ // ident: ID, reason: .Spite, m: <Move>, num: u8
556
+ pub fn activate(self: Self, args: anytype) Error!void {
557
+ if (!enabled) return;
558
+
559
+ assert(args.len <= 4);
560
+ const reason: Activate = args[1];
384
561
  try self.writer.writeAll(&.{
385
- @enumToInt(ArgType.CureStatus),
386
- @bitCast(u8, ident),
387
- value,
388
- @enumToInt(reason),
562
+ @intFromEnum(ArgType.Activate),
563
+ @as(u8, @bitCast(args[0])),
564
+ @intFromEnum(reason),
389
565
  });
566
+ if (args.len == 3) {
567
+ if (reason == .SubstituteBlock) {
568
+ try self.writer.writeByte(@intFromEnum(args[2]));
569
+ } else {
570
+ assert(reason == .Magnitude);
571
+ try self.writer.writeByte(args[2]);
572
+ }
573
+ } else if (args.len == 4) {
574
+ assert(reason == .Spite);
575
+ try self.writer.writeAll(&.{ @intFromEnum(args[2]), args[3] });
576
+ }
390
577
  }
391
578
 
392
- pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
393
- if (!trace) return;
394
- assert(num != 0 and (reason != .Rage or num > 0));
579
+ // -
580
+ pub fn fieldactivate(self: Self, _: anytype) Error!void {
581
+ if (!enabled) return;
582
+
583
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.FieldActivate)});
584
+ }
585
+
586
+ // ident: ID, reason: Start
587
+ // ident: ID, reason: .Disable | .Mimic | .Encore, m: <Move>
588
+ // ident: ID, reason: .TypeChange, types: <Types>, target: ID
589
+ pub fn start(self: Self, args: anytype) Error!void {
590
+ if (!enabled) return;
591
+
592
+ assert(args.len <= 4);
593
+ const reason: Start = args[1];
395
594
  try self.writer.writeAll(&.{
396
- @enumToInt(ArgType.Boost),
397
- @bitCast(u8, ident),
398
- @enumToInt(reason),
399
- @intCast(u8, num + 6),
595
+ @intFromEnum(ArgType.Start),
596
+ @as(u8, @bitCast(args[0])),
597
+ @intFromEnum(reason),
400
598
  });
599
+ if (args.len == 3) {
600
+ assert(reason == .Disable or reason == .Mimic or reason == .Encore);
601
+ try self.writer.writeByte(@intFromEnum(args[2]));
602
+ } else if (args.len == 4) {
603
+ assert(reason == .TypeChange);
604
+ // FIXME: u16 types in Generation II
605
+ try self.writer.writeAll(&.{
606
+ @as(u8, @bitCast(args[2])),
607
+ @as(u8, @bitCast(args[3])),
608
+ });
609
+ }
401
610
  }
402
611
 
403
- pub fn clearallboost(self: Self) Error!void {
404
- if (!trace) return;
405
- try self.writer.writeAll(&.{@enumToInt(ArgType.ClearAllBoost)});
406
- }
612
+ // ident: ID, reason: End
613
+ pub fn end(self: Self, args: anytype) Error!void {
614
+ if (!enabled) return;
407
615
 
408
- pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
409
- if (!trace) return;
410
616
  try self.writer.writeAll(&.{
411
- @enumToInt(ArgType.Fail),
412
- @bitCast(u8, ident),
413
- @enumToInt(reason),
617
+ @intFromEnum(ArgType.End),
618
+ @as(u8, @bitCast(args[0])),
619
+ @intFromEnum(@as(End, args[1])),
414
620
  });
415
621
  }
416
622
 
417
- pub fn miss(self: Self, source: ID) Error!void {
418
- if (!trace) return;
419
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Miss), @bitCast(u8, source) });
623
+ // -
624
+ pub fn ohko(self: Self, _: anytype) Error!void {
625
+ if (!enabled) return;
626
+
627
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
420
628
  }
421
629
 
422
- pub fn hitcount(self: Self, ident: ID, num: u8) Error!void {
423
- if (!trace) return;
424
- try self.writer.writeAll(&.{ @enumToInt(ArgType.HitCount), @bitCast(u8, ident), num });
630
+ // ident: ID
631
+ pub fn crit(self: Self, args: anytype) Error!void {
632
+ if (!enabled) return;
633
+
634
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(args[0])) });
425
635
  }
426
636
 
427
- pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
428
- if (!trace) return;
637
+ // ident: ID
638
+ pub fn supereffective(self: Self, args: anytype) Error!void {
639
+ if (!enabled) return;
640
+
429
641
  try self.writer.writeAll(&.{
430
- @enumToInt(ArgType.Prepare),
431
- @bitCast(u8, source),
432
- @enumToInt(m),
642
+ @intFromEnum(ArgType.SuperEffective),
643
+ @as(u8, @bitCast(args[0])),
433
644
  });
434
645
  }
435
646
 
436
- pub fn mustrecharge(self: Self, ident: ID) Error!void {
437
- if (!trace) return;
438
- try self.writer.writeAll(&.{ @enumToInt(ArgType.MustRecharge), @bitCast(u8, ident) });
439
- }
647
+ // ident: ID
648
+ pub fn resisted(self: Self, args: anytype) Error!void {
649
+ if (!enabled) return;
440
650
 
441
- pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
442
- if (!trace) return;
443
651
  try self.writer.writeAll(&.{
444
- @enumToInt(ArgType.Activate),
445
- @bitCast(u8, ident),
446
- @enumToInt(reason),
652
+ @intFromEnum(ArgType.Resisted),
653
+ @as(u8, @bitCast(args[0])),
447
654
  });
448
655
  }
449
656
 
450
- pub fn fieldactivate(self: Self) Error!void {
451
- if (!trace) return;
452
- try self.writer.writeAll(&.{@enumToInt(ArgType.FieldActivate)});
453
- }
657
+ // ident: ID, reason: Immune
658
+ pub fn immune(self: Self, args: anytype) Error!void {
659
+ if (!enabled) return;
454
660
 
455
- pub fn start(self: Self, ident: ID, reason: Start) Error!void {
456
- if (!trace) return;
457
- assert(@enumToInt(reason) < @enumToInt(Start.TypeChange));
458
661
  try self.writer.writeAll(&.{
459
- @enumToInt(ArgType.Start),
460
- @bitCast(u8, ident),
461
- @enumToInt(reason),
662
+ @intFromEnum(ArgType.Immune),
663
+ @as(u8, @bitCast(args[0])),
664
+ @intFromEnum(@as(Immune, args[1])),
462
665
  });
463
666
  }
464
667
 
465
- pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
466
- if (!trace) return;
668
+ // source: ID, target: ID
669
+ pub fn transform(self: Self, args: anytype) Error!void {
670
+ if (!enabled) return;
671
+
467
672
  try self.writer.writeAll(&.{
468
- @enumToInt(ArgType.Start),
469
- @bitCast(u8, source),
470
- @enumToInt(Start.TypeChange),
471
- @bitCast(u8, types),
472
- @bitCast(u8, target),
673
+ @intFromEnum(ArgType.Transform),
674
+ @as(u8, @bitCast(args[0])),
675
+ @as(u8, @bitCast(args[1])),
473
676
  });
474
677
  }
475
678
 
476
- pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) Error!void {
477
- if (!trace) return;
478
- assert(@enumToInt(reason) > @enumToInt(Start.TypeChange));
679
+ // ident: ID, pokemon: <Pokemon>
680
+ pub fn drag(self: Self, args: anytype) Error!void {
681
+ return switchDrag(self, .Drag, args);
682
+ }
683
+
684
+ // target: ID, item: <Item>, source: ID
685
+ pub fn item(self: Self, args: anytype) Error!void {
686
+ if (!enabled) return;
687
+
479
688
  try self.writer.writeAll(&.{
480
- @enumToInt(ArgType.Start),
481
- @bitCast(u8, ident),
482
- @enumToInt(reason),
483
- @enumToInt(m),
689
+ @intFromEnum(ArgType.Item),
690
+ @as(u8, @bitCast(args[0])),
691
+ @intFromEnum(args[1]),
692
+ @as(u8, @bitCast(args[2])),
484
693
  });
485
694
  }
486
695
 
487
- pub fn end(self: Self, ident: ID, reason: End) Error!void {
488
- if (!trace) return;
696
+ // ident: ID, item: <Item>, reason: EndItem
697
+ pub fn enditem(self: Self, args: anytype) Error!void {
698
+ if (!enabled) return;
699
+
489
700
  try self.writer.writeAll(&.{
490
- @enumToInt(ArgType.End),
491
- @bitCast(u8, ident),
492
- @enumToInt(reason),
701
+ @intFromEnum(ArgType.EndItem),
702
+ @as(u8, @bitCast(args[0])),
703
+ @intFromEnum(args[1]),
704
+ @intFromEnum(@as(EndItem, args[2])),
493
705
  });
494
706
  }
495
707
 
496
- pub fn ohko(self: Self) Error!void {
497
- if (!trace) return;
498
- try self.writer.writeAll(&.{@enumToInt(ArgType.OHKO)});
499
- }
708
+ // source: ID
709
+ pub fn cureteam(self: Self, args: anytype) Error!void {
710
+ if (!enabled) return;
500
711
 
501
- pub fn crit(self: Self, ident: ID) Error!void {
502
- if (!trace) return;
503
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Crit), @bitCast(u8, ident) });
712
+ try self.writer.writeAll(&.{
713
+ @intFromEnum(ArgType.CureTeam),
714
+ @as(u8, @bitCast(args[0])),
715
+ });
504
716
  }
505
717
 
506
- pub fn supereffective(self: Self, ident: ID) Error!void {
507
- if (!trace) return;
508
- try self.writer.writeAll(&.{ @enumToInt(ArgType.SuperEffective), @bitCast(u8, ident) });
718
+ // ident: ID, pokemon: <Pokemon>, reason: SetHP
719
+ pub fn sethp(self: Self, args: anytype) Error!void {
720
+ if (!enabled) return;
721
+
722
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(args[0])) });
723
+ try self.writer.writeInt(u16, args[1].hp, endian);
724
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
725
+ try self.writer.writeAll(&.{args[1].status});
726
+ try self.writer.writeByte(@intFromEnum(@as(SetHP, args[2])));
509
727
  }
510
728
 
511
- pub fn resisted(self: Self, ident: ID) Error!void {
512
- if (!trace) return;
513
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Resisted), @bitCast(u8, ident) });
729
+ // ident: ID, num: i8
730
+ pub fn setboost(self: Self, args: anytype) Error!void {
731
+ if (!enabled) return;
732
+
733
+ try self.writer.writeAll(&.{
734
+ @intFromEnum(ArgType.SetBoost),
735
+ @as(u8, @bitCast(args[0])),
736
+ @as(u8, @intCast(args[1] + 6)),
737
+ });
514
738
  }
515
739
 
516
- pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
517
- if (!trace) return;
740
+ // source: ID, target: ID
741
+ pub fn copyboost(self: Self, args: anytype) Error!void {
742
+ if (!enabled) return;
743
+
518
744
  try self.writer.writeAll(&.{
519
- @enumToInt(ArgType.Immune),
520
- @bitCast(u8, ident),
521
- @enumToInt(reason),
745
+ @intFromEnum(ArgType.CopyBoost),
746
+ @as(u8, @bitCast(args[0])),
747
+ @as(u8, @bitCast(args[1])),
522
748
  });
523
749
  }
524
750
 
525
- pub fn transform(self: Self, source: ID, target: ID) Error!void {
526
- if (!trace) return;
751
+ // player: Player, reason: Side
752
+ pub fn sidestart(self: Self, args: anytype) Error!void {
753
+ if (!enabled) return;
754
+
527
755
  try self.writer.writeAll(&.{
528
- @enumToInt(ArgType.Transform),
529
- @bitCast(u8, source),
530
- @bitCast(u8, target),
756
+ @intFromEnum(ArgType.SideStart),
757
+ @intFromEnum(@as(Player, args[0])),
758
+ @intFromEnum(@as(Side, args[1])),
531
759
  });
532
760
  }
533
761
 
534
- pub fn laststill(self: Self) Error!void {
535
- if (!trace) return;
536
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastStill)});
762
+ // player: Player, reason: Side
763
+ // player: Player, reason: .Spikes, source: ID
764
+ pub fn sideend(self: Self, args: anytype) Error!void {
765
+ if (!enabled) return;
766
+
767
+ assert(args[1] != .Spikes or args.len == 3);
768
+ try self.writer.writeAll(&.{
769
+ @intFromEnum(ArgType.SideEnd),
770
+ @intFromEnum(@as(Player, args[0])),
771
+ @intFromEnum(@as(Side, args[1])),
772
+ });
773
+ if (args.len == 3) try self.writer.writeByte(@as(u8, @bitCast(args[2])));
537
774
  }
538
775
 
539
- pub fn lastmiss(self: Self) Error!void {
540
- if (!trace) return;
541
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastMiss)});
776
+ // ident: ID, move: <Move>
777
+ pub fn singlemove(self: Self, args: anytype) Error!void {
778
+ return single(self, .SingleMove, args);
542
779
  }
543
- };
544
- }
545
780
 
546
- pub const FixedLog = Log(ByteStream.Writer);
781
+ // ident: ID, move: <Move>
782
+ pub fn singleturn(self: Self, args: anytype) Error!void {
783
+ return single(self, .SingleTurn, args);
784
+ }
547
785
 
548
- pub const ByteStream = struct {
549
- buffer: []u8,
550
- pos: usize = 0,
786
+ // ident: ID, move: <Move>
787
+ fn single(self: Self, arg: ArgType, args: anytype) Error!void {
788
+ if (!enabled) return;
551
789
 
552
- pub const Writer = struct {
553
- stream: *ByteStream,
790
+ try self.writer.writeAll(&.{
791
+ @intFromEnum(arg),
792
+ @as(u8, @bitCast(args[0])),
793
+ @intFromEnum(args[1]),
794
+ });
795
+ }
554
796
 
555
- pub const Error = error{NoSpaceLeft};
797
+ // weather: <Weather>, reason: Weather
798
+ pub fn weather(self: Self, args: anytype) Error!void {
799
+ if (!enabled) return;
556
800
 
557
- pub fn writeAll(self: Writer, bytes: []const u8) Error!void {
558
- for (bytes) |b| try self.writeByte(b);
801
+ assert(!(args[0] == .None and args[1] == .Upkeep));
802
+ try self.writer.writeAll(&.{
803
+ @intFromEnum(ArgType.Weather),
804
+ @intFromEnum(args[0]),
805
+ @intFromEnum(@as(Weather, args[1])),
806
+ });
559
807
  }
560
808
 
561
- pub fn writeByte(self: Writer, byte: u8) Error!void {
562
- try self.stream.writeByte(byte);
809
+ // -
810
+ pub fn laststill(self: Self, _: anytype) Error!void {
811
+ if (!enabled) return;
812
+
813
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
563
814
  }
564
815
 
565
- pub fn writeIntNative(self: Writer, comptime T: type, value: T) Error!void {
566
- // TODO: rework this to write directly to the buffer?
567
- var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
568
- std.mem.writeIntNative(T, &bytes, value);
569
- return self.writeAll(&bytes);
816
+ // -
817
+ pub fn lastmiss(self: Self, _: anytype) Error!void {
818
+ if (!enabled) return;
819
+
820
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastMiss)});
570
821
  }
571
822
  };
823
+ }
572
824
 
573
- pub fn writer(self: *ByteStream) Writer {
574
- return .{ .stream = self };
575
- }
825
+ /// `Log` type backed by the optimized `Writer`.
826
+ pub const FixedLog = Log(*Writer);
827
+
828
+ /// Minimal logging helper optimized for efficiently writing the individual protocol bytes into a
829
+ /// fixed buffer. Note that this is **not** a `std.Io.Writer` and should not be used for general
830
+ /// purpose writing.
831
+ pub const Writer = struct {
832
+ buffer: []u8,
833
+ pos: usize = 0,
576
834
 
577
- pub fn writeByte(self: *ByteStream, byte: u8) Writer.Error!void {
835
+ pub const Error = error{NoSpaceLeft};
836
+
837
+ pub fn writeByte(self: *Writer, byte: u8) Error!void {
578
838
  if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
579
839
  self.buffer[self.pos] = byte;
580
840
  self.pos += 1;
581
841
  }
582
842
 
583
- pub fn reset(self: *ByteStream) void {
843
+ pub fn writeAll(self: *Writer, bytes: []const u8) Error!void {
844
+ const npos = self.pos + bytes.len;
845
+ if (npos > self.buffer.len) return error.NoSpaceLeft;
846
+ @memcpy(self.buffer[self.pos..npos], bytes);
847
+ self.pos = npos;
848
+ }
849
+
850
+ pub fn writeInt(self: *Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
851
+ const bytes = @divExact(@typeInfo(T).int.bits, 8);
852
+ const npos = self.pos + bytes;
853
+ if (npos > self.buffer.len) return error.NoSpaceLeft;
854
+ std.mem.writeInt(T, self.buffer[self.pos..][0..bytes], v, end);
855
+ self.pos = npos;
856
+ }
857
+
858
+ pub fn reset(self: *Writer) void {
584
859
  self.pos = 0;
585
860
  }
586
861
  };
587
862
 
588
- pub const Kind = enum { Move, Species, Type, Status };
589
- pub const Formatter = fn (Kind, u8) []const u8;
863
+ /// Null object pattern implementation of `Log` backed by a null writer. Ignores anything sent to
864
+ /// it, though protocol logging should additionally be turned off entirely with `options.log`.
865
+ pub const NULL = Log(NullWriter){ .writer = .{} };
866
+
867
+ const NullWriter = struct {
868
+ pub const Error = error{};
869
+
870
+ pub fn writeByte(self: NullWriter, byte: u8) Error!void {
871
+ _ = .{ self, byte };
872
+ }
873
+
874
+ pub fn writeAll(self: NullWriter, bytes: []const u8) Error!void {
875
+ _ = .{ self, bytes };
876
+ }
877
+
878
+ pub fn writeInt(self: NullWriter, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
879
+ _ = .{ self, T, v, end };
880
+ }
881
+ };
882
+
883
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
590
884
 
591
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
885
+ pub fn format(
886
+ gen: anytype,
887
+ a: []const u8,
888
+ b: ?[]const u8,
889
+ color: bool,
890
+ ) void {
592
891
  print("\n", .{});
593
892
 
594
893
  var i: usize = 0;
595
894
  while (i < a.len) {
596
- const arg = @intToEnum(ArgType, a[i]);
895
+ const arg: ArgType = @enumFromInt(a[i]);
597
896
  const name = switch (arg) {
598
897
  .None => if (color) "\x1b[2m-\x1b[0m" else "-",
599
898
  .LastStill => "|[still]",
@@ -626,6 +925,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
626
925
  .Resisted => "|-resisted|",
627
926
  .Immune => "|-immune|",
628
927
  .Transform => "|-transform|",
928
+ .Drag => "|drag|",
929
+ .Item => "|-item|",
930
+ .EndItem => "|-enditem|",
931
+ .CureTeam => "|-cureteam|",
932
+ .SetHP => "|-sethp|",
933
+ .SetBoost => "|-setboost|",
934
+ .CopyBoost => "|-copyboost|",
935
+ .SideStart => "|-sidestart|",
936
+ .SideEnd => "|-sideend|",
937
+ .SingleMove => "|-singlemove|",
938
+ .SingleTurn => "|-singleturn|",
939
+ .Weather => "|-weather|",
629
940
  else => unreachable,
630
941
  };
631
942
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -639,43 +950,53 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
639
950
  .OHKO,
640
951
  => {},
641
952
  .Move => {
642
- const source = ID.from(@intCast(u4, a[i]));
953
+ const source = ID.from(@intCast(a[i]));
643
954
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
644
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
645
- const target = ID.from(@intCast(u4, a[i]));
955
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
956
+ const target = ID.from(@intCast(a[i]));
646
957
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
647
- const reason = @intToEnum(Move, a[i]);
958
+ const reason: Move = @enumFromInt(a[i]);
648
959
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
649
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
960
+ if (reason == .From) {
961
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
962
+ }
650
963
  },
651
- .Switch => {
652
- const id = ID.from(@intCast(u4, a[i]));
964
+ .Switch, .Drag => {
965
+ const id = ID.from(@intCast(a[i]));
653
966
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
654
- printc(" {s}", .{formatter(.Species, a[i])}, a, b, &i, 1, color);
967
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
968
+ if (@hasDecl(gen, "Gender")) {
969
+ if (a[i] == N(gen.Gender.Unknown)) {
970
+ i += 1;
971
+ } else {
972
+ const gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
973
+ printc(", {s}", .{gender}, a, b, &i, 1, color);
974
+ }
975
+ }
655
976
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
656
977
  switch (endian) {
657
- .Big => {
978
+ .big => {
658
979
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
659
980
  printc(" {d}", .{hp}, a, b, &i, 2, color);
660
981
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
661
982
  printc("/{d}", .{hp}, a, b, &i, 2, color);
662
983
  },
663
- .Little => {
984
+ .little => {
664
985
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
665
986
  printc(" {d}", .{hp}, a, b, &i, 2, color);
666
987
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
667
988
  printc("/{d}", .{hp}, a, b, &i, 2, color);
668
989
  },
669
990
  }
670
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
991
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
671
992
  },
672
993
  .Cant => {
673
- const id = ID.from(@intCast(u4, a[i]));
994
+ const id = ID.from(@intCast(a[i]));
674
995
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
675
- const reason = @intToEnum(Cant, a[i]);
996
+ const reason: Cant = @enumFromInt(a[i]);
676
997
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
677
998
  if (reason == .Disable) {
678
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
999
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
679
1000
  }
680
1001
  },
681
1002
  .Faint,
@@ -684,124 +1005,189 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
684
1005
  .Crit,
685
1006
  .SuperEffective,
686
1007
  .Resisted,
1008
+ .CureTeam,
687
1009
  => {
688
- const id = ID.from(@intCast(u4, a[i]));
1010
+ const id = ID.from(@intCast(a[i]));
689
1011
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
690
1012
  },
691
1013
  .Turn => {
692
1014
  const turn = switch (endian) {
693
- .Big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
694
- .Little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
1015
+ .big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
1016
+ .little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
695
1017
  };
696
1018
  printc(" {d}", .{turn}, a, b, &i, 2, color);
697
1019
  },
698
- .Win => printc(" {s}", .{@tagName(@intToEnum(Player, a[i]))}, a, b, &i, 1, color),
1020
+ .Win => {
1021
+ printc(" {s}", .{@tagName(@as(Player, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1022
+ },
699
1023
  .Damage, .Heal => {
700
- var id = ID.from(@intCast(u4, a[i]));
1024
+ var id = ID.from(@intCast(a[i]));
701
1025
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
702
1026
  switch (endian) {
703
- .Big => {
1027
+ .big => {
704
1028
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
705
1029
  printc(" {d}", .{hp}, a, b, &i, 2, color);
706
1030
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
707
1031
  printc("/{d}", .{hp}, a, b, &i, 2, color);
708
1032
  },
709
- .Little => {
1033
+ .little => {
710
1034
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
711
1035
  printc(" {d}", .{hp}, a, b, &i, 2, color);
712
1036
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
713
1037
  printc("/{d}", .{hp}, a, b, &i, 2, color);
714
1038
  },
715
1039
  }
716
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1040
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
717
1041
  if (arg == .Damage) {
718
- const reason = a[i];
719
- printc(" {s}", .{@tagName(@intToEnum(Damage, reason))}, a, b, &i, 1, color);
720
- if (reason == @enumToInt(Damage.RecoilOf)) {
721
- id = ID.from(@intCast(u4, a[i]));
1042
+ const reason: Damage = @enumFromInt(a[i]);
1043
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1044
+ if (reason == .RecoilOf) {
1045
+ id = ID.from(@intCast(a[i]));
722
1046
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
723
1047
  }
724
1048
  } else {
725
- const reason = @intToEnum(Heal, a[i]);
1049
+ const reason: Heal = @enumFromInt(a[i]);
726
1050
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
727
1051
  if (reason == .Drain) {
728
- id = ID.from(@intCast(u4, a[i]));
1052
+ id = ID.from(@intCast(a[i]));
729
1053
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
730
1054
  }
731
1055
  }
732
1056
  },
733
1057
  .Status => {
734
- const id = ID.from(@intCast(u4, a[i]));
1058
+ const id = ID.from(@intCast(a[i]));
735
1059
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
736
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
737
- const reason = @intToEnum(Status, a[i]);
1060
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1061
+ const reason: Status = @enumFromInt(a[i]);
738
1062
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
739
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1063
+ if (reason == .From) {
1064
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1065
+ }
740
1066
  },
741
1067
  .CureStatus => {
742
- const id = ID.from(@intCast(u4, a[i]));
1068
+ const id = ID.from(@intCast(a[i]));
743
1069
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
744
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
745
- printc(" {s}", .{@tagName(@intToEnum(CureStatus, a[i]))}, a, b, &i, 1, color);
1070
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1071
+ const reason: CureStatus = @enumFromInt(a[i]);
1072
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
746
1073
  },
747
1074
  .Boost => {
748
- const id = ID.from(@intCast(u4, a[i]));
1075
+ const id = ID.from(@intCast(a[i]));
749
1076
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
750
- printc(" {s}", .{@tagName(@intToEnum(Boost, a[i]))}, a, b, &i, 1, color);
1077
+ printc(" {s}", .{@tagName(@as(Boost, @enumFromInt(a[i])))}, a, b, &i, 1, color);
751
1078
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
752
1079
  },
753
1080
  .Fail => {
754
- const id = ID.from(@intCast(u4, a[i]));
1081
+ const id = ID.from(@intCast(a[i]));
755
1082
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
756
- printc(" {s}", .{@tagName(@intToEnum(Fail, a[i]))}, a, b, &i, 1, color);
1083
+ printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
757
1084
  },
758
- .HitCount => {
759
- const id = ID.from(@intCast(u4, a[i]));
1085
+ .HitCount, .SetBoost => {
1086
+ const id = ID.from(@intCast(a[i]));
760
1087
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
761
1088
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
762
1089
  },
763
- .Prepare => {
764
- const id = ID.from(@intCast(u4, a[i]));
1090
+ .Prepare, .SingleMove, .SingleTurn => {
1091
+ const id = ID.from(@intCast(a[i]));
765
1092
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
766
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1093
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
767
1094
  },
768
1095
  .Activate => {
769
- const id = ID.from(@intCast(u4, a[i]));
1096
+ const id = ID.from(@intCast(a[i]));
770
1097
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
771
- printc(" {s}", .{@tagName(@intToEnum(Activate, a[i]))}, a, b, &i, 1, color);
1098
+ const reason: Activate = @enumFromInt(a[i]);
1099
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1100
+ if (reason == .SubstituteBlock) {
1101
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1102
+ } else if (reason == .Magnitude) {
1103
+ printc(" {d}", .{a[i]}, a, b, &i, 1, color);
1104
+ }
772
1105
  },
773
1106
  .Start => {
774
- const id = ID.from(@intCast(u4, a[i]));
1107
+ const id = ID.from(@intCast(a[i]));
775
1108
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
776
1109
  const reason = a[i];
777
- printc(" {s}", .{@tagName(@intToEnum(Start, reason))}, a, b, &i, 1, color);
778
- if (@intToEnum(Start, reason) == .TypeChange) {
779
- const types = @bitCast(gen1.Types, a[i]);
1110
+ printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
1111
+ if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
1112
+ // FIXME
1113
+ const types = @as(gen1.Types, @bitCast(a[i]));
780
1114
  const args = .{
781
- formatter(.Type, @enumToInt(types.type1)),
782
- formatter(.Type, @enumToInt(types.type2)),
1115
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1116
+ formatter(gen, .Type, @intFromEnum(types.type2)),
783
1117
  };
784
1118
  printc(" {s}/{s}", args, a, b, &i, 1, color);
785
- } else if (reason >= @enumToInt(Start.Disable)) {
786
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1119
+ } else if (reason >= @intFromEnum(Start.Disable)) {
1120
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
787
1121
  }
788
1122
  },
789
1123
  .End => {
790
- const id = ID.from(@intCast(u4, a[i]));
1124
+ const id = ID.from(@intCast(a[i]));
791
1125
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
792
- printc(" {s}", .{@tagName(@intToEnum(End, a[i]))}, a, b, &i, 1, color);
1126
+ printc(" {s}", .{@tagName(@as(End, @enumFromInt(a[i])))}, a, b, &i, 1, color);
793
1127
  },
794
1128
  .Immune => {
795
- const id = ID.from(@intCast(u4, a[i]));
1129
+ const id = ID.from(@intCast(a[i]));
796
1130
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
797
- printc(" {s}", .{@tagName(@intToEnum(Immune, a[i]))}, a, b, &i, 1, color);
1131
+ printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
798
1132
  },
799
- .Transform => {
800
- const source = ID.from(@intCast(u4, a[i]));
1133
+ .Transform, .CopyBoost => {
1134
+ const source = ID.from(@intCast(a[i]));
801
1135
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
802
- const target = ID.from(@intCast(u4, a[i]));
1136
+ const target = ID.from(@intCast(a[i]));
803
1137
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
804
1138
  },
1139
+ .Item, .EndItem => {
1140
+ var id = ID.from(@intCast(a[i]));
1141
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1142
+ printc(" {s}", .{formatter(gen, .Item, a[i])}, a, b, &i, 1, color);
1143
+ if (arg == .Item) {
1144
+ id = ID.from(@intCast(a[i]));
1145
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1146
+ } else {
1147
+ const reason: EndItem = @enumFromInt(a[i]);
1148
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1149
+ }
1150
+ },
1151
+ .SetHP => {
1152
+ const id = ID.from(@intCast(a[i]));
1153
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1154
+ switch (endian) {
1155
+ .big => {
1156
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1157
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1158
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1159
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1160
+ },
1161
+ .little => {
1162
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1163
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
1164
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1165
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
1166
+ },
1167
+ }
1168
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
1169
+ printc(" {s}", .{@tagName(@as(SetHP, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1170
+ },
1171
+ .SideStart => {
1172
+ const id = ID.from(@intCast(a[i]));
1173
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1174
+ printc(" {s}", .{@tagName(@as(Side, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1175
+ },
1176
+ .SideEnd => {
1177
+ var id = ID.from(@intCast(a[i]));
1178
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1179
+ const reason: Side = @enumFromInt(a[i]);
1180
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
1181
+ if (reason == .Spikes) {
1182
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
1183
+ id = ID.from(@intCast(a[i]));
1184
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1185
+ }
1186
+ },
1187
+ .Weather => {
1188
+ printc(" {s}", .{formatter(gen, .Weather, a[i])}, a, b, &i, 1, color);
1189
+ printc(" {s}", .{@tagName(@as(Weather, @enumFromInt(a[i])))}, a, b, &i, 1, color);
1190
+ },
805
1191
  else => unreachable,
806
1192
  }
807
1193
  print("\n", .{});
@@ -833,29 +1219,22 @@ fn printc(
833
1219
  }
834
1220
 
835
1221
  pub fn expectLog(
836
- comptime formatter: Formatter,
1222
+ comptime gen: anytype,
837
1223
  expected: []const u8,
838
1224
  actual: []const u8,
839
1225
  offset: usize,
840
1226
  ) !void {
841
- if (!trace) return;
842
- const color = color: {
843
- if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
844
- break :color true;
845
- } else if (std.process.hasEnvVarConstant("NO_COLOR")) {
846
- break :color false;
847
- } else {
848
- break :color std.io.getStdErr().supportsAnsiEscapeCodes();
849
- }
850
- };
1227
+ if (!enabled) return;
1228
+
1229
+ // TODO: ziglang/zig#30833 - read from environment
1230
+ const color = true;
851
1231
 
852
1232
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
853
1233
  error.TestExpectedEqual => {
854
- format(formatter, expected, null, color);
855
- format(formatter, actual, expected, color);
1234
+ format(gen, expected, null, color);
1235
+ format(gen, actual, expected, color);
856
1236
  return err;
857
1237
  },
858
- else => return err,
859
1238
  };
860
1239
  }
861
1240
 
@@ -863,7 +1242,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
863
1242
  for (offset..@min(expected.len, actual.len)) |i| {
864
1243
  if (expected[i] != actual[i]) {
865
1244
  print(
866
- "index {} incorrect. expected 0x{X:0>2}, found 0x{X:0>2}\n",
1245
+ "index {d} incorrect. expected 0x{X:0>2}, found 0x{X:0>2}\n",
867
1246
  .{ i, expected[i], actual[i] },
868
1247
  );
869
1248
  return error.TestExpectedEqual;
@@ -878,85 +1257,97 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
878
1257
  }
879
1258
  }
880
1259
 
881
- const endian = builtin.target.cpu.arch.endian();
882
-
883
1260
  fn N(e: anytype) u8 {
884
- return @enumToInt(e);
1261
+ return @intFromEnum(e);
885
1262
  }
886
1263
 
887
1264
  const p1 = Player.P1;
888
1265
  const p2 = Player.P2;
889
1266
 
890
- const gen1 = struct {
891
- pub usingnamespace @import("../gen1/data.zig");
892
- pub const helpers = @import("../gen1/helpers.zig");
893
- };
1267
+ var buf: [pkmn.LOGS_SIZE]u8 = undefined;
1268
+ var writer: Writer = .{ .buffer = &buf };
1269
+ var log: FixedLog = .{ .writer = &writer };
1270
+
1271
+ const gen1 = pkmn.gen1;
1272
+ const gen2 = pkmn.gen2;
894
1273
 
895
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
896
- var stream = ByteStream{ .buffer = &buf };
897
- var log: FixedLog = .{ .writer = stream.writer() };
1274
+ const M1 = gen1.Move;
1275
+ const S1 = gen1.Species;
898
1276
 
899
- const M = gen1.Move;
900
- const S = gen1.Species;
1277
+ const M2 = gen2.Move;
1278
+ const S2 = gen2.Species;
1279
+ const I2 = gen2.Item;
1280
+ const W2 = gen2.Weather;
901
1281
 
902
1282
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
903
- return expectLog(gen1Formatter, expected, actual, 0);
1283
+ return expectLog(gen1, expected, actual, 0);
904
1284
  }
905
1285
 
906
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1286
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1287
+ return expectLog(gen2, expected, actual, 0);
1288
+ }
1289
+
1290
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
907
1291
  return switch (kind) {
908
- .Move => @tagName(@intToEnum(M, byte)),
909
- .Species => @tagName(@intToEnum(S, byte)),
910
- .Type => @tagName(@intToEnum(gen1.Type, byte)),
911
- .Status => gen1.Status.name(byte),
1292
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1293
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1294
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1295
+ .Status => gen.Status.name(byte),
1296
+ .Item => if (@hasDecl(gen, "Item"))
1297
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1298
+ else
1299
+ unreachable,
1300
+ .Weather => if (@hasDecl(gen, "Weather"))
1301
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1302
+ else
1303
+ unreachable,
912
1304
  };
913
1305
  }
914
1306
 
915
1307
  test "|move|" {
916
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1308
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
917
1309
  try expectLog1(
918
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1310
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
919
1311
  buf[0..5],
920
1312
  );
921
- stream.reset();
1313
+ writer.reset();
922
1314
 
923
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
924
- const wrap = N(M.Wrap);
1315
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
925
1316
  try expectLog1(
926
- &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
1317
+ &.{ N(ArgType.Move), 0b1100, N(M1.Pound), 0b0101, N(Move.From), N(M1.Metronome) },
927
1318
  buf[0..6],
928
1319
  );
929
- stream.reset();
1320
+ writer.reset();
930
1321
 
931
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
932
- try log.laststill();
1322
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1323
+ try log.laststill(.{});
933
1324
  try expectLog1(
934
1325
  &.{
935
1326
  N(ArgType.Move),
936
1327
  0b1100,
937
- N(M.SkullBash),
1328
+ N(M1.SkullBash),
938
1329
  0,
939
1330
  N(Move.None),
940
1331
  N(ArgType.LastStill),
941
1332
  },
942
1333
  buf[0..6],
943
1334
  );
944
- stream.reset();
1335
+ writer.reset();
945
1336
 
946
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
947
- try log.lastmiss();
1337
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1338
+ try log.lastmiss(.{});
948
1339
  try expectLog1(
949
1340
  &.{
950
1341
  N(ArgType.Move),
951
1342
  0b1100,
952
- N(M.Tackle),
1343
+ N(M1.Tackle),
953
1344
  0b0101,
954
1345
  N(Move.None),
955
1346
  N(ArgType.LastMiss),
956
1347
  },
957
1348
  buf[0..6],
958
1349
  );
959
- stream.reset();
1350
+ writer.reset();
960
1351
  }
961
1352
 
962
1353
  test "|switch|" {
@@ -965,333 +1356,495 @@ test "|switch|" {
965
1356
  snorlax.hp = 200;
966
1357
  snorlax.stats.hp = 400;
967
1358
  snorlax.status = gen1.Status.init(.PAR);
968
- try log.switched(p2.ident(3), &snorlax);
1359
+ try log.switched(.{ p2.ident(3), &snorlax });
969
1360
  const par = 0b1000000;
970
1361
  var expected: []const u8 = switch (endian) {
971
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
972
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 200, 0, 144, 1, par },
1362
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1363
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
973
1364
  };
974
1365
  try expectLog1(expected, buf[0..9]);
975
- stream.reset();
1366
+ writer.reset();
976
1367
 
977
1368
  snorlax.level = 100;
978
1369
  snorlax.hp = 0;
979
1370
  snorlax.status = 0;
980
- try log.switched(p2.ident(3), &snorlax);
1371
+ try log.switched(.{ p2.ident(3), &snorlax });
981
1372
  expected = switch (endian) {
982
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
983
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 144, 1, 0 },
1373
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1374
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
984
1375
  };
985
1376
  try expectLog1(expected, buf[0..9]);
986
- stream.reset();
1377
+ writer.reset();
987
1378
 
988
1379
  snorlax.hp = 400;
989
- try log.switched(p2.ident(3), &snorlax);
1380
+ try log.switched(.{ p2.ident(3), &snorlax });
990
1381
  expected = switch (endian) {
991
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
992
- .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 144, 1, 144, 1, 0 },
1382
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1383
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
993
1384
  };
994
1385
  try expectLog1(expected, buf[0..9]);
995
- stream.reset();
1386
+ writer.reset();
1387
+
1388
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1389
+ blissey.level = 91;
1390
+ blissey.hp = 200;
1391
+ blissey.stats.hp = 400;
1392
+ blissey.status = gen2.Status.init(.PAR);
1393
+ try log.switched(.{ p2.ident(3), &blissey });
1394
+ expected = &(.{
1395
+ N(ArgType.Switch),
1396
+ 0b1011,
1397
+ N(S2.Blissey),
1398
+ N(gen2.Gender.Female),
1399
+ 91,
1400
+ } ++ switch (endian) {
1401
+ .big => .{ 0, 200, 1, 144, par },
1402
+ .little => .{ 200, 0, 144, 1, par },
1403
+ });
1404
+ try expectLog2(expected, buf[0..10]);
1405
+ writer.reset();
996
1406
  }
997
1407
 
998
1408
  test "|cant|" {
999
- try log.cant(p2.ident(6), .Bound);
1409
+ try log.cant(.{ p2.ident(6), .Bound });
1000
1410
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1001
- stream.reset();
1411
+ writer.reset();
1002
1412
 
1003
- try log.disabled(p1.ident(2), M.Earthquake);
1004
- try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M.Earthquake) }, buf[0..4]);
1005
- stream.reset();
1413
+ try log.cant(.{ p1.ident(2), .Disable, M1.Earthquake });
1414
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1415
+ writer.reset();
1006
1416
  }
1007
1417
 
1008
1418
  test "|faint|" {
1009
- try log.faint(p2.ident(2), false);
1419
+ try log.faint(.{ p2.ident(2), false });
1010
1420
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1011
- stream.reset();
1421
+ writer.reset();
1012
1422
 
1013
- try log.faint(p2.ident(2), true);
1423
+ try log.faint(.{ p2.ident(2), true });
1014
1424
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1015
- stream.reset();
1425
+ writer.reset();
1016
1426
  }
1017
1427
 
1018
1428
  test "|turn|" {
1019
- try log.turn(42);
1020
- var expected = switch (endian) {
1021
- .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1022
- .Little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1429
+ try log.turn(.{42});
1430
+ const expected = switch (endian) {
1431
+ .big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1432
+ .little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1023
1433
  };
1024
1434
  try expectLog1(expected, buf[0..4]);
1025
- stream.reset();
1435
+ writer.reset();
1026
1436
  }
1027
1437
 
1028
1438
  test "|win|" {
1029
- try log.win(.P2);
1439
+ try log.win(.{.P2});
1030
1440
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1031
- stream.reset();
1441
+ writer.reset();
1032
1442
  }
1033
1443
 
1034
1444
  test "|tie|" {
1035
- try log.tie();
1445
+ try log.tie(.{});
1036
1446
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1037
- stream.reset();
1447
+ writer.reset();
1038
1448
  }
1039
1449
 
1040
1450
  test "|-damage|" {
1041
1451
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1042
1452
  chansey.hp = 612;
1043
1453
  chansey.status = gen1.Status.slp(1);
1044
- try log.damage(p2.ident(2), &chansey, .None);
1454
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1045
1455
  var expected: []const u8 = switch (endian) {
1046
- .Big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1047
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1456
+ .big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1457
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1048
1458
  };
1049
1459
  try expectLog1(expected, buf[0..8]);
1050
- stream.reset();
1460
+ writer.reset();
1051
1461
 
1052
1462
  chansey.hp = 100;
1053
1463
  chansey.stats.hp = 256;
1054
1464
  chansey.status = 0;
1055
- try log.damage(p2.ident(2), &chansey, .Confusion);
1465
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1056
1466
  expected = switch (endian) {
1057
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1058
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1467
+ .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1468
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1059
1469
  };
1060
1470
  try expectLog1(expected, buf[0..8]);
1061
- stream.reset();
1471
+ writer.reset();
1062
1472
 
1063
1473
  chansey.status = gen1.Status.init(.PSN);
1064
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1474
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1065
1475
  expected = switch (endian) {
1066
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1067
- .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1476
+ .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1477
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1068
1478
  };
1069
1479
  try expectLog1(expected, buf[0..9]);
1070
- stream.reset();
1480
+ writer.reset();
1071
1481
  }
1072
1482
 
1073
1483
  test "|-heal|" {
1074
1484
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1075
1485
  chansey.hp = 612;
1076
1486
  chansey.status = gen1.Status.slp(1);
1077
- try log.heal(p2.ident(2), &chansey, .None);
1487
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1078
1488
  var expected: []const u8 = switch (endian) {
1079
- .Big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1080
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1489
+ .big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1490
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1081
1491
  };
1082
1492
  try expectLog1(expected, buf[0..8]);
1083
- stream.reset();
1493
+ writer.reset();
1084
1494
 
1085
1495
  chansey.hp = 100;
1086
1496
  chansey.stats.hp = 256;
1087
1497
  chansey.status = 0;
1088
- try log.heal(p2.ident(2), &chansey, .Silent);
1498
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1089
1499
  expected = switch (endian) {
1090
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1091
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1500
+ .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1501
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1092
1502
  };
1093
1503
  try expectLog1(expected, buf[0..8]);
1094
- stream.reset();
1504
+ writer.reset();
1095
1505
 
1096
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1506
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1097
1507
  expected = switch (endian) {
1098
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1099
- .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1508
+ .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1509
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1100
1510
  };
1101
1511
  try expectLog1(expected, buf[0..9]);
1102
- stream.reset();
1512
+ writer.reset();
1103
1513
  }
1104
1514
 
1105
1515
  test "|-status|" {
1106
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1516
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1107
1517
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1108
- stream.reset();
1518
+ writer.reset();
1109
1519
 
1110
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1520
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1111
1521
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1112
- stream.reset();
1522
+ writer.reset();
1113
1523
 
1114
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1524
+ try log.status(.{ p1.ident(1), gen1.Status.init(.PAR), .From, M1.BodySlam });
1115
1525
  try expectLog1(
1116
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1526
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1117
1527
  buf[0..5],
1118
1528
  );
1119
- stream.reset();
1529
+ writer.reset();
1120
1530
  }
1121
1531
 
1122
1532
  test "|-curestatus|" {
1123
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1533
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1124
1534
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1125
- stream.reset();
1535
+ writer.reset();
1126
1536
 
1127
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1537
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1128
1538
  try expectLog1(&.{
1129
1539
  N(ArgType.CureStatus),
1130
1540
  0b0010,
1131
1541
  0b10001000,
1132
1542
  N(CureStatus.Silent),
1133
1543
  }, buf[0..4]);
1134
- stream.reset();
1544
+ writer.reset();
1135
1545
  }
1136
1546
 
1137
1547
  test "|-boost|" {
1138
- try log.boost(p2.ident(6), .Speed, 2);
1548
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1139
1549
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1140
- stream.reset();
1550
+ writer.reset();
1141
1551
 
1142
- try log.boost(p1.ident(2), .Rage, 1);
1552
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1143
1553
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1144
- stream.reset();
1554
+ writer.reset();
1145
1555
 
1146
- try log.boost(p2.ident(3), .Defense, -2);
1556
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1147
1557
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1148
- stream.reset();
1558
+ writer.reset();
1149
1559
  }
1150
1560
 
1151
1561
  test "|-clearallboost|" {
1152
- try log.clearallboost();
1562
+ try log.clearallboost(.{});
1153
1563
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1154
- stream.reset();
1564
+ writer.reset();
1155
1565
  }
1156
1566
 
1157
1567
  test "|-fail|" {
1158
- try log.fail(p2.ident(6), .None);
1568
+ try log.fail(.{ p2.ident(6), .None });
1159
1569
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1160
- stream.reset();
1570
+ writer.reset();
1161
1571
 
1162
- try log.fail(p2.ident(6), .Sleep);
1572
+ try log.fail(.{ p2.ident(6), .Sleep });
1163
1573
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1164
- stream.reset();
1574
+ writer.reset();
1165
1575
 
1166
- try log.fail(p2.ident(6), .Substitute);
1576
+ try log.fail(.{ p2.ident(6), .Substitute });
1167
1577
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1168
- stream.reset();
1578
+ writer.reset();
1169
1579
 
1170
- try log.fail(p2.ident(6), .Weak);
1580
+ try log.fail(.{ p2.ident(6), .Weak });
1171
1581
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1172
- stream.reset();
1582
+ writer.reset();
1173
1583
  }
1174
1584
 
1175
1585
  test "|-miss|" {
1176
- try log.miss(p2.ident(4));
1586
+ try log.miss(.{p2.ident(4)});
1177
1587
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1178
- stream.reset();
1588
+ writer.reset();
1179
1589
  }
1180
1590
  test "|-hitcount|" {
1181
- try log.hitcount(p2.ident(1), 5);
1591
+ try log.hitcount(.{ p2.ident(1), 5 });
1182
1592
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1183
- stream.reset();
1593
+ writer.reset();
1184
1594
  }
1185
1595
 
1186
1596
  test "|-prepare|" {
1187
- try log.prepare(p2.ident(2), M.Dig);
1188
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1189
- stream.reset();
1597
+ try log.prepare(.{ p2.ident(2), M1.Dig });
1598
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1599
+ writer.reset();
1190
1600
  }
1191
1601
 
1192
1602
  test "|-mustrecharge|" {
1193
- try log.mustrecharge(p1.ident(6));
1603
+ try log.mustrecharge(.{p1.ident(6)});
1194
1604
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1195
- stream.reset();
1605
+ writer.reset();
1196
1606
  }
1197
1607
 
1198
1608
  test "|-activate|" {
1199
- try log.activate(p1.ident(2), .Struggle);
1609
+ try log.activate(.{ p1.ident(2), .Struggle });
1200
1610
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1201
- stream.reset();
1611
+ writer.reset();
1202
1612
 
1203
- try log.activate(p2.ident(4), .Mist);
1613
+ try log.activate(.{ p2.ident(4), .Mist });
1204
1614
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1205
- stream.reset();
1615
+ writer.reset();
1206
1616
 
1207
- try log.activate(p2.ident(6), .Substitute);
1617
+ try log.activate(.{ p2.ident(6), .Substitute });
1208
1618
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1209
- stream.reset();
1619
+ writer.reset();
1210
1620
 
1211
- try log.activate(p1.ident(2), .Splash);
1621
+ try log.activate(.{ p1.ident(2), .Splash });
1212
1622
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1213
- stream.reset();
1623
+ writer.reset();
1624
+
1625
+ try log.activate(.{ p1.ident(4), .SubstituteBlock, M2.Spite });
1626
+ try expectLog1(&.{
1627
+ N(ArgType.Activate),
1628
+ 0b0100,
1629
+ N(Activate.SubstituteBlock),
1630
+ N(M2.Spite),
1631
+ }, buf[0..4]);
1632
+ writer.reset();
1633
+
1634
+ try log.activate(.{ p1.ident(3), .Magnitude, 5 });
1635
+ try expectLog1(&.{ N(ArgType.Activate), 0b0011, N(Activate.Magnitude), 5 }, buf[0..4]);
1636
+ writer.reset();
1214
1637
  }
1215
1638
 
1216
1639
  test "|-fieldactivate|" {
1217
- try log.fieldactivate();
1640
+ try log.fieldactivate(.{});
1218
1641
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1219
- stream.reset();
1642
+ writer.reset();
1220
1643
  }
1221
1644
 
1222
1645
  test "|-start|" {
1223
- try log.start(p2.ident(6), .Bide);
1646
+ try log.start(.{ p2.ident(6), .Bide });
1224
1647
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1225
- stream.reset();
1648
+ writer.reset();
1226
1649
 
1227
- try log.start(p1.ident(2), .ConfusionSilent);
1650
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1228
1651
  try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1229
- stream.reset();
1230
-
1231
- try log.typechange(p2.ident(6), gen1.Types{ .type1 = .Fire, .type2 = .Fire }, p2.ident(5));
1652
+ writer.reset();
1653
+
1654
+ try log.start(.{
1655
+ p2.ident(6),
1656
+ .TypeChange,
1657
+ gen1.Types{ .type1 = .Fire, .type2 = .Fire },
1658
+ p2.ident(5),
1659
+ });
1232
1660
  try expectLog1(
1233
1661
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1234
1662
  buf[0..5],
1235
1663
  );
1236
- stream.reset();
1237
-
1238
- try log.typechange(p1.ident(2), gen1.Types{ .type1 = .Bug, .type2 = .Poison }, p2.ident(4));
1664
+ writer.reset();
1665
+
1666
+ try log.start(.{
1667
+ p1.ident(2),
1668
+ .TypeChange,
1669
+ gen1.Types{ .type1 = .Bug, .type2 = .Poison },
1670
+ p2.ident(4),
1671
+ });
1239
1672
  try expectLog1(
1240
1673
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1241
1674
  buf[0..5],
1242
1675
  );
1243
- stream.reset();
1676
+ writer.reset();
1244
1677
 
1245
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1246
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M.Surf) }, buf[0..4]);
1247
- stream.reset();
1678
+ try log.start(.{ p1.ident(2), .Disable, M1.Surf });
1679
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1680
+ writer.reset();
1248
1681
 
1249
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1250
- try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M.Surf) }, buf[0..4]);
1251
- stream.reset();
1682
+ try log.start(.{ p1.ident(2), .Mimic, M1.Surf });
1683
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1684
+ writer.reset();
1252
1685
  }
1253
1686
 
1254
1687
  test "|-end|" {
1255
- try log.end(p2.ident(6), .Bide);
1688
+ try log.end(.{ p2.ident(6), .Bide });
1256
1689
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1257
- stream.reset();
1690
+ writer.reset();
1258
1691
 
1259
- try log.end(p1.ident(2), .ConfusionSilent);
1692
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1260
1693
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1261
- stream.reset();
1694
+ writer.reset();
1262
1695
  }
1263
1696
 
1264
1697
  test "|-ohko|" {
1265
- try log.ohko();
1698
+ try log.ohko(.{});
1266
1699
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1267
- stream.reset();
1700
+ writer.reset();
1268
1701
  }
1702
+
1269
1703
  test "|-crit|" {
1270
- try log.crit(p2.ident(5));
1704
+ try log.crit(.{p2.ident(5)});
1271
1705
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1272
- stream.reset();
1706
+ writer.reset();
1273
1707
  }
1708
+
1274
1709
  test "|-supereffective|" {
1275
- try log.supereffective(p1.ident(1));
1710
+ try log.supereffective(.{p1.ident(1)});
1276
1711
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1277
- stream.reset();
1712
+ writer.reset();
1278
1713
  }
1714
+
1279
1715
  test "|-resisted|" {
1280
- try log.resisted(p2.ident(2));
1716
+ try log.resisted(.{p2.ident(2)});
1281
1717
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1282
- stream.reset();
1718
+ writer.reset();
1283
1719
  }
1720
+
1284
1721
  test "|-immune|" {
1285
- try log.immune(p1.ident(3), .None);
1722
+ try log.immune(.{ p1.ident(3), .None });
1286
1723
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1287
- stream.reset();
1724
+ writer.reset();
1288
1725
 
1289
- try log.immune(p2.ident(2), .OHKO);
1726
+ try log.immune(.{ p2.ident(2), .OHKO });
1290
1727
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1291
- stream.reset();
1728
+ writer.reset();
1292
1729
  }
1730
+
1293
1731
  test "|-transform|" {
1294
- try log.transform(p2.ident(4), p1.ident(5));
1732
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1295
1733
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1296
- stream.reset();
1734
+ writer.reset();
1735
+ }
1736
+
1737
+ test "|drag|" {
1738
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1739
+ blissey.level = 91;
1740
+ blissey.hp = 200;
1741
+ blissey.stats.hp = 400;
1742
+ blissey.status = gen2.Status.init(.PAR);
1743
+ try log.drag(.{ p2.ident(3), &blissey });
1744
+ const par = 0b1000000;
1745
+ const expected = &(.{
1746
+ N(ArgType.Drag),
1747
+ 0b1011,
1748
+ N(S2.Blissey),
1749
+ N(gen2.Gender.Female),
1750
+ 91,
1751
+ } ++ switch (endian) {
1752
+ .big => .{ 0, 200, 1, 144, par },
1753
+ .little => .{ 200, 0, 144, 1, par },
1754
+ });
1755
+ try expectLog2(expected, buf[0..10]);
1756
+ writer.reset();
1757
+ }
1758
+
1759
+ test "|-item|" {
1760
+ try log.item(.{ p2.ident(2), I2.GoldBerry, p1.ident(4) });
1761
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1762
+ writer.reset();
1763
+ }
1764
+
1765
+ test "|-enditem|" {
1766
+ try log.enditem(.{ p1.ident(1), I2.BerserkGene, .None });
1767
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1768
+ writer.reset();
1769
+
1770
+ try log.enditem(.{ p2.ident(4), I2.PRZCureBerry, .Eat });
1771
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1772
+ writer.reset();
1773
+ }
1774
+
1775
+ test "|-cureteam|" {
1776
+ try log.cureteam(.{p2.ident(5)});
1777
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1778
+ writer.reset();
1779
+ }
1780
+
1781
+ test "|-sethp|" {
1782
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1783
+ blissey.hp = 612;
1784
+ try log.sethp(.{ p2.ident(2), &blissey, .Silent });
1785
+ const expected: []const u8 = switch (endian) {
1786
+ .big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1787
+ .little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1788
+ };
1789
+ try expectLog2(expected, buf[0..8]);
1790
+ writer.reset();
1791
+ }
1792
+
1793
+ test "|-setboost|" {
1794
+ try log.setboost(.{ p2.ident(6), 6 });
1795
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1796
+ writer.reset();
1797
+
1798
+ try log.setboost(.{ p2.ident(3), 2 });
1799
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1800
+ writer.reset();
1801
+ }
1802
+
1803
+ test "|-copyboost|" {
1804
+ try log.copyboost(.{ p2.ident(3), p1.ident(6) });
1805
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1806
+ writer.reset();
1807
+ }
1808
+
1809
+ test "|-sidestart|" {
1810
+ try log.sidestart(.{ .P2, .Reflect });
1811
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1812
+ writer.reset();
1813
+ }
1814
+
1815
+ test "|-sideend|" {
1816
+ try log.sideend(.{ .P2, .LightScreen });
1817
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1818
+ writer.reset();
1819
+
1820
+ try log.sideend(.{ .P1, .Spikes, p1.ident(3) });
1821
+ try expectLog2(&.{
1822
+ N(ArgType.SideEnd),
1823
+ 0,
1824
+ N(Side.Spikes),
1825
+ 0b0011,
1826
+ }, buf[0..4]);
1827
+ writer.reset();
1828
+ }
1829
+
1830
+ test "|-singlemove|" {
1831
+ try log.singlemove(.{ p2.ident(2), M2.DestinyBond });
1832
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1833
+ writer.reset();
1834
+ }
1835
+
1836
+ test "|-singleturn|" {
1837
+ try log.singleturn(.{ p1.ident(3), M2.Protect });
1838
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1839
+ writer.reset();
1840
+ }
1841
+
1842
+ test "|-weather|" {
1843
+ try log.weather(.{ W2.Rain, .Upkeep });
1844
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1845
+ writer.reset();
1846
+
1847
+ try log.weather(.{ W2.None, .None });
1848
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1849
+ writer.reset();
1297
1850
  }