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

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 +77 -23
  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 +20 -1
  27. package/build/pkg/protocol.js +123 -102
  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 +273 -120
  40. package/package.json +64 -123
  41. package/src/bin/install-pkmn-engine +47 -21
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/{binding → bindings}/node.zig +15 -32
  45. package/src/lib/bindings/wasm.zig +50 -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 +32 -11
  49. package/src/lib/common/js.zig +3 -7
  50. package/src/lib/common/optional.zig +19 -18
  51. package/src/lib/common/options.zig +6 -0
  52. package/src/lib/common/protocol.zig +919 -430
  53. package/src/lib/common/rational.zig +341 -254
  54. package/src/lib/common/rng.zig +22 -15
  55. package/src/lib/common/util.zig +33 -1
  56. package/src/lib/gen1/calc.zig +706 -177
  57. package/src/lib/gen1/chance.zig +428 -302
  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 +60 -75
  62. package/src/lib/gen1/helpers.zig +90 -563
  63. package/src/lib/gen1/mechanics.zig +682 -600
  64. package/src/lib/gen2/calc.zig +150 -0
  65. package/src/lib/gen2/chance.zig +896 -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 +121 -96
  71. package/src/lib/gen2/generated.zig +801 -0
  72. package/src/lib/gen2/helpers.zig +53 -38
  73. package/src/lib/gen2/mechanics.zig +3580 -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 +93 -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 -892
  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);
@@ -701,33 +952,43 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
701
952
  .Move => {
702
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);
955
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
705
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 => {
964
+ .Switch, .Drag => {
712
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
994
  const id = ID.from(@intCast(a[i]));
@@ -735,7 +996,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
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
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
  },
@@ -762,20 +1024,20 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
762
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);
@@ -795,15 +1057,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
795
1057
  .Status => {
796
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
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
  },
@@ -818,20 +1082,26 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
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 => {
1085
+ .HitCount, .SetBoost => {
822
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 => {
1090
+ .Prepare, .SingleMove, .SingleTurn => {
827
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
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
1107
  const id = ID.from(@intCast(a[i]));
@@ -839,14 +1109,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
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 => {
@@ -859,12 +1130,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
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 => {
1133
+ .Transform, .CopyBoost => {
863
1134
  const source = ID.from(@intCast(a[i]));
864
1135
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
865
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 = 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
+ },
868
1191
  else => unreachable,
869
1192
  }
870
1193
  print("\n", .{});
@@ -896,30 +1219,22 @@ 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
+ // TODO: ziglang/zig#30833 - read from environment
1230
+ const color = true;
915
1231
 
916
1232
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
917
1233
  error.TestExpectedEqual => {
918
- format(formatter, expected, null, color);
919
- format(formatter, actual, expected, color);
1234
+ format(gen, expected, null, color);
1235
+ format(gen, actual, expected, color);
920
1236
  return err;
921
1237
  },
922
- else => return err,
923
1238
  };
924
1239
  }
925
1240
 
@@ -927,7 +1242,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
927
1242
  for (offset..@min(expected.len, actual.len)) |i| {
928
1243
  if (expected[i] != actual[i]) {
929
1244
  print(
930
- "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",
931
1246
  .{ i, expected[i], actual[i] },
932
1247
  );
933
1248
  return error.TestExpectedEqual;
@@ -942,8 +1257,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
942
1257
  }
943
1258
  }
944
1259
 
945
- const endian = builtin.target.cpu.arch.endian();
946
-
947
1260
  fn N(e: anytype) u8 {
948
1261
  return @intFromEnum(e);
949
1262
  }
@@ -951,76 +1264,90 @@ fn N(e: anytype) u8 {
951
1264
  const p1 = Player.P1;
952
1265
  const p2 = Player.P2;
953
1266
 
954
- const gen1 = struct {
955
- pub usingnamespace @import("../gen1/data.zig");
956
- pub const helpers = @import("../gen1/helpers.zig");
957
- };
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;
958
1273
 
959
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
960
- var stream = ByteStream{ .buffer = &buf };
961
- var log: FixedLog = .{ .writer = stream.writer() };
1274
+ const M1 = gen1.Move;
1275
+ const S1 = gen1.Species;
962
1276
 
963
- const M = gen1.Move;
964
- const S = gen1.Species;
1277
+ const M2 = gen2.Move;
1278
+ const S2 = gen2.Species;
1279
+ const I2 = gen2.Item;
1280
+ const W2 = gen2.Weather;
965
1281
 
966
1282
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
967
- return expectLog(gen1Formatter, expected, actual, 0);
1283
+ return expectLog(gen1, expected, actual, 0);
968
1284
  }
969
1285
 
970
- 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 {
971
1291
  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),
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,
976
1304
  };
977
1305
  }
978
1306
 
979
1307
  test "|move|" {
980
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1308
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
981
1309
  try expectLog1(
982
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1310
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
983
1311
  buf[0..5],
984
1312
  );
985
- stream.reset();
1313
+ writer.reset();
986
1314
 
987
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
988
- const wrap = N(M.Wrap);
1315
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
989
1316
  try expectLog1(
990
- &.{ 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) },
991
1318
  buf[0..6],
992
1319
  );
993
- stream.reset();
1320
+ writer.reset();
994
1321
 
995
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
996
- try log.laststill();
1322
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1323
+ try log.laststill(.{});
997
1324
  try expectLog1(
998
1325
  &.{
999
1326
  N(ArgType.Move),
1000
1327
  0b1100,
1001
- N(M.SkullBash),
1328
+ N(M1.SkullBash),
1002
1329
  0,
1003
1330
  N(Move.None),
1004
1331
  N(ArgType.LastStill),
1005
1332
  },
1006
1333
  buf[0..6],
1007
1334
  );
1008
- stream.reset();
1335
+ writer.reset();
1009
1336
 
1010
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1011
- try log.lastmiss();
1337
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1338
+ try log.lastmiss(.{});
1012
1339
  try expectLog1(
1013
1340
  &.{
1014
1341
  N(ArgType.Move),
1015
1342
  0b1100,
1016
- N(M.Tackle),
1343
+ N(M1.Tackle),
1017
1344
  0b0101,
1018
1345
  N(Move.None),
1019
1346
  N(ArgType.LastMiss),
1020
1347
  },
1021
1348
  buf[0..6],
1022
1349
  );
1023
- stream.reset();
1350
+ writer.reset();
1024
1351
  }
1025
1352
 
1026
1353
  test "|switch|" {
@@ -1029,333 +1356,495 @@ test "|switch|" {
1029
1356
  snorlax.hp = 200;
1030
1357
  snorlax.stats.hp = 400;
1031
1358
  snorlax.status = gen1.Status.init(.PAR);
1032
- try log.switched(p2.ident(3), &snorlax);
1359
+ try log.switched(.{ p2.ident(3), &snorlax });
1033
1360
  const par = 0b1000000;
1034
1361
  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 },
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 },
1037
1364
  };
1038
1365
  try expectLog1(expected, buf[0..9]);
1039
- stream.reset();
1366
+ writer.reset();
1040
1367
 
1041
1368
  snorlax.level = 100;
1042
1369
  snorlax.hp = 0;
1043
1370
  snorlax.status = 0;
1044
- try log.switched(p2.ident(3), &snorlax);
1371
+ try log.switched(.{ p2.ident(3), &snorlax });
1045
1372
  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 },
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 },
1048
1375
  };
1049
1376
  try expectLog1(expected, buf[0..9]);
1050
- stream.reset();
1377
+ writer.reset();
1051
1378
 
1052
1379
  snorlax.hp = 400;
1053
- try log.switched(p2.ident(3), &snorlax);
1380
+ try log.switched(.{ p2.ident(3), &snorlax });
1054
1381
  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 },
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 },
1057
1384
  };
1058
1385
  try expectLog1(expected, buf[0..9]);
1059
- 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();
1060
1406
  }
1061
1407
 
1062
1408
  test "|cant|" {
1063
- try log.cant(p2.ident(6), .Bound);
1409
+ try log.cant(.{ p2.ident(6), .Bound });
1064
1410
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1065
- stream.reset();
1411
+ writer.reset();
1066
1412
 
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();
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();
1070
1416
  }
1071
1417
 
1072
1418
  test "|faint|" {
1073
- try log.faint(p2.ident(2), false);
1419
+ try log.faint(.{ p2.ident(2), false });
1074
1420
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1075
- stream.reset();
1421
+ writer.reset();
1076
1422
 
1077
- try log.faint(p2.ident(2), true);
1423
+ try log.faint(.{ p2.ident(2), true });
1078
1424
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1079
- stream.reset();
1425
+ writer.reset();
1080
1426
  }
1081
1427
 
1082
1428
  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) },
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) },
1087
1433
  };
1088
1434
  try expectLog1(expected, buf[0..4]);
1089
- stream.reset();
1435
+ writer.reset();
1090
1436
  }
1091
1437
 
1092
1438
  test "|win|" {
1093
- try log.win(.P2);
1439
+ try log.win(.{.P2});
1094
1440
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1095
- stream.reset();
1441
+ writer.reset();
1096
1442
  }
1097
1443
 
1098
1444
  test "|tie|" {
1099
- try log.tie();
1445
+ try log.tie(.{});
1100
1446
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1101
- stream.reset();
1447
+ writer.reset();
1102
1448
  }
1103
1449
 
1104
1450
  test "|-damage|" {
1105
1451
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1106
1452
  chansey.hp = 612;
1107
1453
  chansey.status = gen1.Status.slp(1);
1108
- try log.damage(p2.ident(2), &chansey, .None);
1454
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1109
1455
  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) },
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) },
1112
1458
  };
1113
1459
  try expectLog1(expected, buf[0..8]);
1114
- stream.reset();
1460
+ writer.reset();
1115
1461
 
1116
1462
  chansey.hp = 100;
1117
1463
  chansey.stats.hp = 256;
1118
1464
  chansey.status = 0;
1119
- try log.damage(p2.ident(2), &chansey, .Confusion);
1465
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1120
1466
  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) },
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) },
1123
1469
  };
1124
1470
  try expectLog1(expected, buf[0..8]);
1125
- stream.reset();
1471
+ writer.reset();
1126
1472
 
1127
1473
  chansey.status = gen1.Status.init(.PSN);
1128
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1474
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1129
1475
  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 },
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 },
1132
1478
  };
1133
1479
  try expectLog1(expected, buf[0..9]);
1134
- stream.reset();
1480
+ writer.reset();
1135
1481
  }
1136
1482
 
1137
1483
  test "|-heal|" {
1138
1484
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1139
1485
  chansey.hp = 612;
1140
1486
  chansey.status = gen1.Status.slp(1);
1141
- try log.heal(p2.ident(2), &chansey, .None);
1487
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1142
1488
  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) },
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) },
1145
1491
  };
1146
1492
  try expectLog1(expected, buf[0..8]);
1147
- stream.reset();
1493
+ writer.reset();
1148
1494
 
1149
1495
  chansey.hp = 100;
1150
1496
  chansey.stats.hp = 256;
1151
1497
  chansey.status = 0;
1152
- try log.heal(p2.ident(2), &chansey, .Silent);
1498
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1153
1499
  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) },
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) },
1156
1502
  };
1157
1503
  try expectLog1(expected, buf[0..8]);
1158
- stream.reset();
1504
+ writer.reset();
1159
1505
 
1160
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1506
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1161
1507
  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 },
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 },
1164
1510
  };
1165
1511
  try expectLog1(expected, buf[0..9]);
1166
- stream.reset();
1512
+ writer.reset();
1167
1513
  }
1168
1514
 
1169
1515
  test "|-status|" {
1170
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1516
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1171
1517
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1172
- stream.reset();
1518
+ writer.reset();
1173
1519
 
1174
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1520
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1175
1521
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1176
- stream.reset();
1522
+ writer.reset();
1177
1523
 
1178
- 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 });
1179
1525
  try expectLog1(
1180
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1526
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1181
1527
  buf[0..5],
1182
1528
  );
1183
- stream.reset();
1529
+ writer.reset();
1184
1530
  }
1185
1531
 
1186
1532
  test "|-curestatus|" {
1187
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1533
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1188
1534
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1189
- stream.reset();
1535
+ writer.reset();
1190
1536
 
1191
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1537
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1192
1538
  try expectLog1(&.{
1193
1539
  N(ArgType.CureStatus),
1194
1540
  0b0010,
1195
1541
  0b10001000,
1196
1542
  N(CureStatus.Silent),
1197
1543
  }, buf[0..4]);
1198
- stream.reset();
1544
+ writer.reset();
1199
1545
  }
1200
1546
 
1201
1547
  test "|-boost|" {
1202
- try log.boost(p2.ident(6), .Speed, 2);
1548
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1203
1549
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1204
- stream.reset();
1550
+ writer.reset();
1205
1551
 
1206
- try log.boost(p1.ident(2), .Rage, 1);
1552
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1207
1553
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1208
- stream.reset();
1554
+ writer.reset();
1209
1555
 
1210
- try log.boost(p2.ident(3), .Defense, -2);
1556
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1211
1557
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1212
- stream.reset();
1558
+ writer.reset();
1213
1559
  }
1214
1560
 
1215
1561
  test "|-clearallboost|" {
1216
- try log.clearallboost();
1562
+ try log.clearallboost(.{});
1217
1563
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1218
- stream.reset();
1564
+ writer.reset();
1219
1565
  }
1220
1566
 
1221
1567
  test "|-fail|" {
1222
- try log.fail(p2.ident(6), .None);
1568
+ try log.fail(.{ p2.ident(6), .None });
1223
1569
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1224
- stream.reset();
1570
+ writer.reset();
1225
1571
 
1226
- try log.fail(p2.ident(6), .Sleep);
1572
+ try log.fail(.{ p2.ident(6), .Sleep });
1227
1573
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1228
- stream.reset();
1574
+ writer.reset();
1229
1575
 
1230
- try log.fail(p2.ident(6), .Substitute);
1576
+ try log.fail(.{ p2.ident(6), .Substitute });
1231
1577
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1232
- stream.reset();
1578
+ writer.reset();
1233
1579
 
1234
- try log.fail(p2.ident(6), .Weak);
1580
+ try log.fail(.{ p2.ident(6), .Weak });
1235
1581
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1236
- stream.reset();
1582
+ writer.reset();
1237
1583
  }
1238
1584
 
1239
1585
  test "|-miss|" {
1240
- try log.miss(p2.ident(4));
1586
+ try log.miss(.{p2.ident(4)});
1241
1587
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1242
- stream.reset();
1588
+ writer.reset();
1243
1589
  }
1244
1590
  test "|-hitcount|" {
1245
- try log.hitcount(p2.ident(1), 5);
1591
+ try log.hitcount(.{ p2.ident(1), 5 });
1246
1592
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1247
- stream.reset();
1593
+ writer.reset();
1248
1594
  }
1249
1595
 
1250
1596
  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();
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();
1254
1600
  }
1255
1601
 
1256
1602
  test "|-mustrecharge|" {
1257
- try log.mustrecharge(p1.ident(6));
1603
+ try log.mustrecharge(.{p1.ident(6)});
1258
1604
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1259
- stream.reset();
1605
+ writer.reset();
1260
1606
  }
1261
1607
 
1262
1608
  test "|-activate|" {
1263
- try log.activate(p1.ident(2), .Struggle);
1609
+ try log.activate(.{ p1.ident(2), .Struggle });
1264
1610
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1265
- stream.reset();
1611
+ writer.reset();
1266
1612
 
1267
- try log.activate(p2.ident(4), .Mist);
1613
+ try log.activate(.{ p2.ident(4), .Mist });
1268
1614
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1269
- stream.reset();
1615
+ writer.reset();
1270
1616
 
1271
- try log.activate(p2.ident(6), .Substitute);
1617
+ try log.activate(.{ p2.ident(6), .Substitute });
1272
1618
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1273
- stream.reset();
1619
+ writer.reset();
1274
1620
 
1275
- try log.activate(p1.ident(2), .Splash);
1621
+ try log.activate(.{ p1.ident(2), .Splash });
1276
1622
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1277
- 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();
1278
1637
  }
1279
1638
 
1280
1639
  test "|-fieldactivate|" {
1281
- try log.fieldactivate();
1640
+ try log.fieldactivate(.{});
1282
1641
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1283
- stream.reset();
1642
+ writer.reset();
1284
1643
  }
1285
1644
 
1286
1645
  test "|-start|" {
1287
- try log.start(p2.ident(6), .Bide);
1646
+ try log.start(.{ p2.ident(6), .Bide });
1288
1647
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1289
- stream.reset();
1648
+ writer.reset();
1290
1649
 
1291
- try log.start(p1.ident(2), .ConfusionSilent);
1650
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1292
1651
  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));
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
+ });
1296
1660
  try expectLog1(
1297
1661
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1298
1662
  buf[0..5],
1299
1663
  );
1300
- stream.reset();
1301
-
1302
- 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
+ });
1303
1672
  try expectLog1(
1304
1673
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1305
1674
  buf[0..5],
1306
1675
  );
1307
- stream.reset();
1676
+ writer.reset();
1308
1677
 
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();
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();
1312
1681
 
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();
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();
1316
1685
  }
1317
1686
 
1318
1687
  test "|-end|" {
1319
- try log.end(p2.ident(6), .Bide);
1688
+ try log.end(.{ p2.ident(6), .Bide });
1320
1689
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1321
- stream.reset();
1690
+ writer.reset();
1322
1691
 
1323
- try log.end(p1.ident(2), .ConfusionSilent);
1692
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1324
1693
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1325
- stream.reset();
1694
+ writer.reset();
1326
1695
  }
1327
1696
 
1328
1697
  test "|-ohko|" {
1329
- try log.ohko();
1698
+ try log.ohko(.{});
1330
1699
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1331
- stream.reset();
1700
+ writer.reset();
1332
1701
  }
1702
+
1333
1703
  test "|-crit|" {
1334
- try log.crit(p2.ident(5));
1704
+ try log.crit(.{p2.ident(5)});
1335
1705
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1336
- stream.reset();
1706
+ writer.reset();
1337
1707
  }
1708
+
1338
1709
  test "|-supereffective|" {
1339
- try log.supereffective(p1.ident(1));
1710
+ try log.supereffective(.{p1.ident(1)});
1340
1711
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1341
- stream.reset();
1712
+ writer.reset();
1342
1713
  }
1714
+
1343
1715
  test "|-resisted|" {
1344
- try log.resisted(p2.ident(2));
1716
+ try log.resisted(.{p2.ident(2)});
1345
1717
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1346
- stream.reset();
1718
+ writer.reset();
1347
1719
  }
1720
+
1348
1721
  test "|-immune|" {
1349
- try log.immune(p1.ident(3), .None);
1722
+ try log.immune(.{ p1.ident(3), .None });
1350
1723
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1351
- stream.reset();
1724
+ writer.reset();
1352
1725
 
1353
- try log.immune(p2.ident(2), .OHKO);
1726
+ try log.immune(.{ p2.ident(2), .OHKO });
1354
1727
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1355
- stream.reset();
1728
+ writer.reset();
1356
1729
  }
1730
+
1357
1731
  test "|-transform|" {
1358
- try log.transform(p2.ident(4), p1.ident(5));
1732
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1359
1733
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1360
- 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();
1361
1850
  }