@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.da4a0515

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