@pkmn/engine 0.1.0-dev.d3e796d5 → 0.1.0-dev.d4119c62

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