@pkmn/engine 0.1.0-dev.7963b917 → 0.1.0-dev.79957401

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