@pkmn/engine 0.1.0-dev.3a273268 → 0.1.0-dev.3a62fd35

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