@pkmn/engine 0.1.0-dev.942dbfa4 → 0.1.0-dev.95e81bfe

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