@pkmn/engine 0.1.0-dev.954f650d → 0.1.0-dev.95e81bfe

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +37 -14
  3. package/build/pkg/addon.js.map +1 -1
  4. package/build/pkg/data/ids.json +12 -12
  5. package/build/pkg/data/layout.json +2 -2
  6. package/build/pkg/data/protocol.json +69 -12
  7. package/build/pkg/data.d.ts +3 -2
  8. package/build/pkg/data.js.map +1 -1
  9. package/build/pkg/gen1.js +2 -2
  10. package/build/pkg/gen1.js.map +1 -1
  11. package/build/pkg/index.d.ts +2 -1
  12. package/build/pkg/index.js.map +1 -1
  13. package/build/pkg/protocol.js +57 -38
  14. package/build/pkg/protocol.js.map +1 -1
  15. package/build/test/display.js.map +1 -1
  16. package/build/tools/debug.js +3 -1
  17. package/build/tools/debug.js.map +1 -1
  18. package/build.zig +80 -70
  19. package/package.json +43 -23
  20. package/src/bin/install-pkmn-engine +14 -14
  21. package/src/lib/common/data.zig +9 -9
  22. package/src/lib/common/optional.zig +9 -2
  23. package/src/lib/common/protocol.zig +734 -318
  24. package/src/lib/common/rational.zig +12 -8
  25. package/src/lib/common/rng.zig +25 -16
  26. package/src/lib/gen1/README.md +28 -28
  27. package/src/lib/gen1/calc.zig +9 -9
  28. package/src/lib/gen1/chance.zig +72 -48
  29. package/src/lib/gen1/data/moves.zig +24 -10
  30. package/src/lib/gen1/data/species.zig +3 -3
  31. package/src/lib/gen1/data/types.zig +7 -7
  32. package/src/lib/gen1/data.zig +41 -41
  33. package/src/lib/gen1/helpers.zig +33 -48
  34. package/src/lib/gen1/mechanics.zig +340 -355
  35. package/src/lib/gen2/README.md +18 -72
  36. package/src/lib/gen2/calc.zig +143 -0
  37. package/src/lib/gen2/chance.zig +1314 -0
  38. package/src/lib/gen2/data/items.zig +12 -18
  39. package/src/lib/gen2/data/moves.zig +69 -36
  40. package/src/lib/gen2/data/species.zig +1 -1
  41. package/src/lib/gen2/data/types.zig +134 -31
  42. package/src/lib/gen2/data.zig +52 -44
  43. package/src/lib/gen2/generated.zig +806 -0
  44. package/src/lib/gen2/mechanics.zig +2935 -244
  45. package/src/lib/{binding/node.zig → node.zig} +10 -13
  46. package/src/lib/pkmn.zig +26 -3
  47. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
@@ -1,8 +1,10 @@
1
1
  const std = @import("std");
2
2
  const builtin = @import("builtin");
3
3
 
4
- const data = @import("./data.zig");
5
- const options = @import("./options.zig");
4
+ const pkmn = @import("../pkmn.zig");
5
+
6
+ const data = @import("data.zig");
7
+ const options = @import("options.zig");
6
8
 
7
9
  const assert = std.debug.assert;
8
10
  const print = std.debug.print;
@@ -12,6 +14,8 @@ const enabled = options.log;
12
14
  const Player = data.Player;
13
15
  const ID = data.ID;
14
16
 
17
+ const endian = builtin.cpu.arch.endian();
18
+
15
19
  pub const ArgType = enum(u8) {
16
20
  None,
17
21
 
@@ -69,7 +73,7 @@ pub const ArgType = enum(u8) {
69
73
  EndAbility,
70
74
  ClearNegativeBoost,
71
75
  FormeChange,
72
- NoTarget,
76
+ LastNoTarget, // FIXME
73
77
 
74
78
  // Gen 4
75
79
  SwapBoost,
@@ -102,6 +106,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])));
552
715
  }
553
716
 
554
- pub fn crit(self: Self, ident: ID) Error!void {
717
+ // ident: ID, num: i8
718
+ pub fn setboost(self: Self, args: anytype) Error!void {
555
719
  if (!enabled) return;
556
720
 
557
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(ident)) });
721
+ try self.writer.writeAll(&.{
722
+ @intFromEnum(ArgType.SetBoost),
723
+ @as(u8, @bitCast(args[0])),
724
+ @as(u8, @intCast(args[1] + 6)),
725
+ });
558
726
  }
559
727
 
560
- pub fn supereffective(self: Self, ident: ID) Error!void {
728
+ // source: ID, target: ID
729
+ pub fn copyboost(self: Self, args: anytype) Error!void {
561
730
  if (!enabled) return;
562
731
 
563
732
  try self.writer.writeAll(&.{
564
- @intFromEnum(ArgType.SuperEffective),
565
- @as(u8, @bitCast(ident)),
733
+ @intFromEnum(ArgType.CopyBoost),
734
+ @as(u8, @bitCast(args[0])),
735
+ @as(u8, @bitCast(args[1])),
566
736
  });
567
737
  }
568
738
 
569
- pub fn resisted(self: Self, ident: ID) Error!void {
739
+ // player: Player, reason: Side
740
+ pub fn sidestart(self: Self, args: anytype) Error!void {
570
741
  if (!enabled) return;
571
742
 
572
743
  try self.writer.writeAll(&.{
573
- @intFromEnum(ArgType.Resisted),
574
- @as(u8, @bitCast(ident)),
744
+ @intFromEnum(ArgType.SideStart),
745
+ @intFromEnum(@as(Player, args[0])),
746
+ @intFromEnum(@as(Side, args[1])),
575
747
  });
576
748
  }
577
749
 
578
- pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
750
+ // player: Player, reason: Side
751
+ // player: Player, reason: .Spikes, source: ID
752
+ pub fn sideend(self: Self, args: anytype) Error!void {
579
753
  if (!enabled) return;
580
754
 
755
+ assert(args[1] != .Spikes or args.len == 3);
581
756
  try self.writer.writeAll(&.{
582
- @intFromEnum(ArgType.Immune),
583
- @as(u8, @bitCast(ident)),
584
- @intFromEnum(reason),
757
+ @intFromEnum(ArgType.SideEnd),
758
+ @intFromEnum(@as(Player, args[0])),
759
+ @intFromEnum(@as(Side, args[1])),
585
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);
586
772
  }
587
773
 
588
- pub fn transform(self: Self, source: ID, target: ID) Error!void {
774
+ // ident: ID, move: <Move>
775
+ fn single(self: Self, arg: ArgType, args: anytype) Error!void {
589
776
  if (!enabled) return;
590
777
 
591
778
  try self.writer.writeAll(&.{
592
- @intFromEnum(ArgType.Transform),
593
- @as(u8, @bitCast(source)),
594
- @as(u8, @bitCast(target)),
779
+ @intFromEnum(arg),
780
+ @as(u8, @bitCast(args[0])),
781
+ @intFromEnum(args[1]),
595
782
  });
596
783
  }
597
784
 
598
- pub fn laststill(self: Self) Error!void {
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));
790
+ try self.writer.writeAll(&.{
791
+ @intFromEnum(ArgType.Weather),
792
+ @intFromEnum(args[0]),
793
+ @intFromEnum(@as(Weather, args[1])),
794
+ });
795
+ }
796
+
797
+ // -
798
+ pub fn laststill(self: Self, _: anytype) Error!void {
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);
@@ -732,19 +945,19 @@ pub fn format(
732
945
  if (a[i] == N(gen.Gender.Unknown)) {
733
946
  i += 1;
734
947
  } else {
735
- var gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
948
+ const gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
736
949
  printc(", {s}", .{gender}, a, b, &i, 1, color);
737
950
  }
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", .{});
@@ -929,14 +1202,11 @@ pub fn expectLog(
929
1202
  ) !void {
930
1203
  if (!enabled) return;
931
1204
 
932
- const color = color: {
933
- if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
934
- break :color true;
935
- } else if (std.process.hasEnvVarConstant("NO_COLOR")) {
936
- break :color false;
937
- } else {
938
- break :color std.io.getStdErr().supportsAnsiEscapeCodes();
939
- }
1205
+ const color = if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR"))
1206
+ true
1207
+ else switch (std.io.tty.detectConfig(std.io.getStdErr())) {
1208
+ .escape_codes => true,
1209
+ else => false,
940
1210
  };
941
1211
 
942
1212
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
@@ -968,8 +1238,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
968
1238
  }
969
1239
  }
970
1240
 
971
- const endian = builtin.target.cpu.arch.endian();
972
-
973
1241
  fn N(e: anytype) u8 {
974
1242
  return @intFromEnum(e);
975
1243
  }
@@ -977,22 +1245,20 @@ fn N(e: anytype) u8 {
977
1245
  const p1 = Player.P1;
978
1246
  const p2 = Player.P2;
979
1247
 
980
- const gen1 = struct {
981
- pub usingnamespace @import("../gen1/data.zig");
982
- pub const helpers = @import("../gen1/helpers.zig");
983
- };
1248
+ var buf: [pkmn.LOGS_SIZE]u8 = undefined;
1249
+ var stream: ByteStream = .{ .buffer = &buf };
1250
+ var log: FixedLog = .{ .writer = stream.writer() };
984
1251
 
985
- const gen2 = struct {
986
- pub usingnamespace @import("../gen2/data.zig");
987
- pub const helpers = @import("../gen2/helpers.zig");
988
- };
1252
+ const gen1 = pkmn.gen1;
1253
+ const gen2 = pkmn.gen2;
989
1254
 
990
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
991
- var stream = ByteStream{ .buffer = &buf };
992
- var log: FixedLog = .{ .writer = stream.writer() };
1255
+ const M1 = gen1.Move;
1256
+ const S1 = gen1.Species;
993
1257
 
994
- const M = gen1.Move;
995
- const S = gen1.Species;
1258
+ const M2 = gen2.Move;
1259
+ const S2 = gen2.Species;
1260
+ const I2 = gen2.Item;
1261
+ const W2 = gen2.Weather;
996
1262
 
997
1263
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
998
1264
  return expectLog(gen1, expected, actual, 0);
@@ -1008,32 +1274,39 @@ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
1008
1274
  .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1009
1275
  .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1010
1276
  .Status => gen.Status.name(byte),
1277
+ .Item => if (@hasDecl(gen, "Item"))
1278
+ @tagName(@as(gen.Item, @enumFromInt(byte)))
1279
+ else
1280
+ unreachable,
1281
+ .Weather => if (@hasDecl(gen, "Weather"))
1282
+ @tagName(@as(gen.Weather, @enumFromInt(byte)))
1283
+ else
1284
+ unreachable,
1011
1285
  };
1012
1286
  }
1013
1287
 
1014
1288
  test "|move|" {
1015
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1289
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
1016
1290
  try expectLog1(
1017
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1291
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
1018
1292
  buf[0..5],
1019
1293
  );
1020
1294
  stream.reset();
1021
1295
 
1022
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
1023
- const wrap = N(M.Wrap);
1296
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
1024
1297
  try expectLog1(
1025
- &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
1298
+ &.{ N(ArgType.Move), 0b1100, N(M1.Pound), 0b0101, N(Move.From), N(M1.Metronome) },
1026
1299
  buf[0..6],
1027
1300
  );
1028
1301
  stream.reset();
1029
1302
 
1030
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
1031
- try log.laststill();
1303
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1304
+ try log.laststill(.{});
1032
1305
  try expectLog1(
1033
1306
  &.{
1034
1307
  N(ArgType.Move),
1035
1308
  0b1100,
1036
- N(M.SkullBash),
1309
+ N(M1.SkullBash),
1037
1310
  0,
1038
1311
  N(Move.None),
1039
1312
  N(ArgType.LastStill),
@@ -1042,13 +1315,13 @@ test "|move|" {
1042
1315
  );
1043
1316
  stream.reset();
1044
1317
 
1045
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
1046
- try log.lastmiss();
1318
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1319
+ try log.lastmiss(.{});
1047
1320
  try expectLog1(
1048
1321
  &.{
1049
1322
  N(ArgType.Move),
1050
1323
  0b1100,
1051
- N(M.Tackle),
1324
+ N(M1.Tackle),
1052
1325
  0b0101,
1053
1326
  N(Move.None),
1054
1327
  N(ArgType.LastMiss),
@@ -1064,11 +1337,11 @@ test "|switch|" {
1064
1337
  snorlax.hp = 200;
1065
1338
  snorlax.stats.hp = 400;
1066
1339
  snorlax.status = gen1.Status.init(.PAR);
1067
- try log.switched(p2.ident(3), &snorlax);
1340
+ try log.switched(.{ p2.ident(3), &snorlax });
1068
1341
  const par = 0b1000000;
1069
1342
  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 },
1343
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1344
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1072
1345
  };
1073
1346
  try expectLog1(expected, buf[0..9]);
1074
1347
  stream.reset();
@@ -1076,19 +1349,19 @@ test "|switch|" {
1076
1349
  snorlax.level = 100;
1077
1350
  snorlax.hp = 0;
1078
1351
  snorlax.status = 0;
1079
- try log.switched(p2.ident(3), &snorlax);
1352
+ try log.switched(.{ p2.ident(3), &snorlax });
1080
1353
  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 },
1354
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1355
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1083
1356
  };
1084
1357
  try expectLog1(expected, buf[0..9]);
1085
1358
  stream.reset();
1086
1359
 
1087
1360
  snorlax.hp = 400;
1088
- try log.switched(p2.ident(3), &snorlax);
1361
+ try log.switched(.{ p2.ident(3), &snorlax });
1089
1362
  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 },
1363
+ .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1364
+ .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1092
1365
  };
1093
1366
  try expectLog1(expected, buf[0..9]);
1094
1367
  stream.reset();
@@ -1098,59 +1371,59 @@ test "|switch|" {
1098
1371
  blissey.hp = 200;
1099
1372
  blissey.stats.hp = 400;
1100
1373
  blissey.status = gen2.Status.init(.PAR);
1101
- try log.switched(p2.ident(3), &blissey);
1374
+ try log.switched(.{ p2.ident(3), &blissey });
1102
1375
  expected = &(.{
1103
1376
  N(ArgType.Switch),
1104
1377
  0b1011,
1105
- N(gen2.Species.Blissey),
1378
+ N(S2.Blissey),
1106
1379
  N(gen2.Gender.Female),
1107
1380
  91,
1108
1381
  } ++ switch (endian) {
1109
- .Big => .{ 0, 200, 1, 144, par },
1110
- .Little => .{ 200, 0, 144, 1, par },
1382
+ .big => .{ 0, 200, 1, 144, par },
1383
+ .little => .{ 200, 0, 144, 1, par },
1111
1384
  });
1112
1385
  try expectLog2(expected, buf[0..10]);
1113
1386
  stream.reset();
1114
1387
  }
1115
1388
 
1116
1389
  test "|cant|" {
1117
- try log.cant(p2.ident(6), .Bound);
1390
+ try log.cant(.{ p2.ident(6), .Bound });
1118
1391
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1119
1392
  stream.reset();
1120
1393
 
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]);
1394
+ try log.cant(.{ p1.ident(2), .Disable, M1.Earthquake });
1395
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M1.Earthquake) }, buf[0..4]);
1123
1396
  stream.reset();
1124
1397
  }
1125
1398
 
1126
1399
  test "|faint|" {
1127
- try log.faint(p2.ident(2), false);
1400
+ try log.faint(.{ p2.ident(2), false });
1128
1401
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1129
1402
  stream.reset();
1130
1403
 
1131
- try log.faint(p2.ident(2), true);
1404
+ try log.faint(.{ p2.ident(2), true });
1132
1405
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1133
1406
  stream.reset();
1134
1407
  }
1135
1408
 
1136
1409
  test "|turn|" {
1137
- try log.turn(42);
1138
- var expected = switch (endian) {
1139
- .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1140
- .Little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1410
+ try log.turn(.{42});
1411
+ const expected = switch (endian) {
1412
+ .big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1413
+ .little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1141
1414
  };
1142
1415
  try expectLog1(expected, buf[0..4]);
1143
1416
  stream.reset();
1144
1417
  }
1145
1418
 
1146
1419
  test "|win|" {
1147
- try log.win(.P2);
1420
+ try log.win(.{.P2});
1148
1421
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1149
1422
  stream.reset();
1150
1423
  }
1151
1424
 
1152
1425
  test "|tie|" {
1153
- try log.tie();
1426
+ try log.tie(.{});
1154
1427
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1155
1428
  stream.reset();
1156
1429
  }
@@ -1159,10 +1432,10 @@ test "|-damage|" {
1159
1432
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1160
1433
  chansey.hp = 612;
1161
1434
  chansey.status = gen1.Status.slp(1);
1162
- try log.damage(p2.ident(2), &chansey, .None);
1435
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1163
1436
  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) },
1437
+ .big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1438
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1166
1439
  };
1167
1440
  try expectLog1(expected, buf[0..8]);
1168
1441
  stream.reset();
@@ -1170,19 +1443,19 @@ test "|-damage|" {
1170
1443
  chansey.hp = 100;
1171
1444
  chansey.stats.hp = 256;
1172
1445
  chansey.status = 0;
1173
- try log.damage(p2.ident(2), &chansey, .Confusion);
1446
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1174
1447
  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) },
1448
+ .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1449
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1177
1450
  };
1178
1451
  try expectLog1(expected, buf[0..8]);
1179
1452
  stream.reset();
1180
1453
 
1181
1454
  chansey.status = gen1.Status.init(.PSN);
1182
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1455
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1183
1456
  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 },
1457
+ .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1458
+ .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1186
1459
  };
1187
1460
  try expectLog1(expected, buf[0..9]);
1188
1461
  stream.reset();
@@ -1192,10 +1465,10 @@ test "|-heal|" {
1192
1465
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1193
1466
  chansey.hp = 612;
1194
1467
  chansey.status = gen1.Status.slp(1);
1195
- try log.heal(p2.ident(2), &chansey, .None);
1468
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1196
1469
  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) },
1470
+ .big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1471
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1199
1472
  };
1200
1473
  try expectLog1(expected, buf[0..8]);
1201
1474
  stream.reset();
@@ -1203,46 +1476,46 @@ test "|-heal|" {
1203
1476
  chansey.hp = 100;
1204
1477
  chansey.stats.hp = 256;
1205
1478
  chansey.status = 0;
1206
- try log.heal(p2.ident(2), &chansey, .Silent);
1479
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1207
1480
  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) },
1481
+ .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1482
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1210
1483
  };
1211
1484
  try expectLog1(expected, buf[0..8]);
1212
1485
  stream.reset();
1213
1486
 
1214
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1487
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1215
1488
  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 },
1489
+ .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1490
+ .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1218
1491
  };
1219
1492
  try expectLog1(expected, buf[0..9]);
1220
1493
  stream.reset();
1221
1494
  }
1222
1495
 
1223
1496
  test "|-status|" {
1224
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1497
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1225
1498
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1226
1499
  stream.reset();
1227
1500
 
1228
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1501
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1229
1502
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1230
1503
  stream.reset();
1231
1504
 
1232
- try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1505
+ try log.status(.{ p1.ident(1), gen1.Status.init(.PAR), .From, M1.BodySlam });
1233
1506
  try expectLog1(
1234
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1507
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1235
1508
  buf[0..5],
1236
1509
  );
1237
1510
  stream.reset();
1238
1511
  }
1239
1512
 
1240
1513
  test "|-curestatus|" {
1241
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1514
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1242
1515
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1243
1516
  stream.reset();
1244
1517
 
1245
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1518
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1246
1519
  try expectLog1(&.{
1247
1520
  N(ArgType.CureStatus),
1248
1521
  0b0010,
@@ -1253,163 +1526,306 @@ test "|-curestatus|" {
1253
1526
  }
1254
1527
 
1255
1528
  test "|-boost|" {
1256
- try log.boost(p2.ident(6), .Speed, 2);
1529
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1257
1530
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1258
1531
  stream.reset();
1259
1532
 
1260
- try log.boost(p1.ident(2), .Rage, 1);
1533
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1261
1534
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1262
1535
  stream.reset();
1263
1536
 
1264
- try log.boost(p2.ident(3), .Defense, -2);
1537
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1265
1538
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1266
1539
  stream.reset();
1267
1540
  }
1268
1541
 
1269
1542
  test "|-clearallboost|" {
1270
- try log.clearallboost();
1543
+ try log.clearallboost(.{});
1271
1544
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1272
1545
  stream.reset();
1273
1546
  }
1274
1547
 
1275
1548
  test "|-fail|" {
1276
- try log.fail(p2.ident(6), .None);
1549
+ try log.fail(.{ p2.ident(6), .None });
1277
1550
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1278
1551
  stream.reset();
1279
1552
 
1280
- try log.fail(p2.ident(6), .Sleep);
1553
+ try log.fail(.{ p2.ident(6), .Sleep });
1281
1554
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1282
1555
  stream.reset();
1283
1556
 
1284
- try log.fail(p2.ident(6), .Substitute);
1557
+ try log.fail(.{ p2.ident(6), .Substitute });
1285
1558
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1286
1559
  stream.reset();
1287
1560
 
1288
- try log.fail(p2.ident(6), .Weak);
1561
+ try log.fail(.{ p2.ident(6), .Weak });
1289
1562
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1290
1563
  stream.reset();
1291
1564
  }
1292
1565
 
1293
1566
  test "|-miss|" {
1294
- try log.miss(p2.ident(4));
1567
+ try log.miss(.{p2.ident(4)});
1295
1568
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1296
1569
  stream.reset();
1297
1570
  }
1298
1571
  test "|-hitcount|" {
1299
- try log.hitcount(p2.ident(1), 5);
1572
+ try log.hitcount(.{ p2.ident(1), 5 });
1300
1573
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1301
1574
  stream.reset();
1302
1575
  }
1303
1576
 
1304
1577
  test "|-prepare|" {
1305
- try log.prepare(p2.ident(2), M.Dig);
1306
- try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1578
+ try log.prepare(.{ p2.ident(2), M1.Dig });
1579
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M1.Dig) }, buf[0..3]);
1307
1580
  stream.reset();
1308
1581
  }
1309
1582
 
1310
1583
  test "|-mustrecharge|" {
1311
- try log.mustrecharge(p1.ident(6));
1584
+ try log.mustrecharge(.{p1.ident(6)});
1312
1585
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1313
1586
  stream.reset();
1314
1587
  }
1315
1588
 
1316
1589
  test "|-activate|" {
1317
- try log.activate(p1.ident(2), .Struggle);
1590
+ try log.activate(.{ p1.ident(2), .Struggle });
1318
1591
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1319
1592
  stream.reset();
1320
1593
 
1321
- try log.activate(p2.ident(4), .Mist);
1594
+ try log.activate(.{ p2.ident(4), .Mist });
1322
1595
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1323
1596
  stream.reset();
1324
1597
 
1325
- try log.activate(p2.ident(6), .Substitute);
1598
+ try log.activate(.{ p2.ident(6), .Substitute });
1326
1599
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1327
1600
  stream.reset();
1328
1601
 
1329
- try log.activate(p1.ident(2), .Splash);
1602
+ try log.activate(.{ p1.ident(2), .Splash });
1330
1603
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1331
1604
  stream.reset();
1605
+
1606
+ try log.activate(.{ p1.ident(4), .SubstituteBlock, M2.Spite });
1607
+ try expectLog1(&.{
1608
+ N(ArgType.Activate),
1609
+ 0b0100,
1610
+ N(Activate.SubstituteBlock),
1611
+ N(M2.Spite),
1612
+ }, buf[0..4]);
1613
+ stream.reset();
1614
+
1615
+ try log.activate(.{ p1.ident(3), .Magnitude, 5 });
1616
+ try expectLog1(&.{ N(ArgType.Activate), 0b0011, N(Activate.Magnitude), 5 }, buf[0..4]);
1617
+ stream.reset();
1332
1618
  }
1333
1619
 
1334
1620
  test "|-fieldactivate|" {
1335
- try log.fieldactivate();
1621
+ try log.fieldactivate(.{});
1336
1622
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1337
1623
  stream.reset();
1338
1624
  }
1339
1625
 
1340
1626
  test "|-start|" {
1341
- try log.start(p2.ident(6), .Bide);
1627
+ try log.start(.{ p2.ident(6), .Bide });
1342
1628
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1343
1629
  stream.reset();
1344
1630
 
1345
- try log.start(p1.ident(2), .ConfusionSilent);
1631
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1346
1632
  try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1347
1633
  stream.reset();
1348
1634
 
1349
- try log.typechange(p2.ident(6), gen1.Types{ .type1 = .Fire, .type2 = .Fire }, p2.ident(5));
1635
+ try log.start(.{
1636
+ p2.ident(6),
1637
+ .TypeChange,
1638
+ gen1.Types{ .type1 = .Fire, .type2 = .Fire },
1639
+ p2.ident(5),
1640
+ });
1350
1641
  try expectLog1(
1351
1642
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1352
1643
  buf[0..5],
1353
1644
  );
1354
1645
  stream.reset();
1355
1646
 
1356
- try log.typechange(p1.ident(2), gen1.Types{ .type1 = .Bug, .type2 = .Poison }, p2.ident(4));
1647
+ try log.start(.{
1648
+ p1.ident(2),
1649
+ .TypeChange,
1650
+ gen1.Types{ .type1 = .Bug, .type2 = .Poison },
1651
+ p2.ident(4),
1652
+ });
1357
1653
  try expectLog1(
1358
1654
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1359
1655
  buf[0..5],
1360
1656
  );
1361
1657
  stream.reset();
1362
1658
 
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]);
1659
+ try log.start(.{ p1.ident(2), .Disable, M1.Surf });
1660
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M1.Surf) }, buf[0..4]);
1365
1661
  stream.reset();
1366
1662
 
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]);
1663
+ try log.start(.{ p1.ident(2), .Mimic, M1.Surf });
1664
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M1.Surf) }, buf[0..4]);
1369
1665
  stream.reset();
1370
1666
  }
1371
1667
 
1372
1668
  test "|-end|" {
1373
- try log.end(p2.ident(6), .Bide);
1669
+ try log.end(.{ p2.ident(6), .Bide });
1374
1670
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1375
1671
  stream.reset();
1376
1672
 
1377
- try log.end(p1.ident(2), .ConfusionSilent);
1673
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1378
1674
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1379
1675
  stream.reset();
1380
1676
  }
1381
1677
 
1382
1678
  test "|-ohko|" {
1383
- try log.ohko();
1679
+ try log.ohko(.{});
1384
1680
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1385
1681
  stream.reset();
1386
1682
  }
1683
+
1387
1684
  test "|-crit|" {
1388
- try log.crit(p2.ident(5));
1685
+ try log.crit(.{p2.ident(5)});
1389
1686
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1390
1687
  stream.reset();
1391
1688
  }
1689
+
1392
1690
  test "|-supereffective|" {
1393
- try log.supereffective(p1.ident(1));
1691
+ try log.supereffective(.{p1.ident(1)});
1394
1692
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1395
1693
  stream.reset();
1396
1694
  }
1695
+
1397
1696
  test "|-resisted|" {
1398
- try log.resisted(p2.ident(2));
1697
+ try log.resisted(.{p2.ident(2)});
1399
1698
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1400
1699
  stream.reset();
1401
1700
  }
1701
+
1402
1702
  test "|-immune|" {
1403
- try log.immune(p1.ident(3), .None);
1703
+ try log.immune(.{ p1.ident(3), .None });
1404
1704
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1405
1705
  stream.reset();
1406
1706
 
1407
- try log.immune(p2.ident(2), .OHKO);
1707
+ try log.immune(.{ p2.ident(2), .OHKO });
1408
1708
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1409
1709
  stream.reset();
1410
1710
  }
1711
+
1411
1712
  test "|-transform|" {
1412
- try log.transform(p2.ident(4), p1.ident(5));
1713
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1413
1714
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1414
1715
  stream.reset();
1415
1716
  }
1717
+
1718
+ test "|drag|" {
1719
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1720
+ blissey.level = 91;
1721
+ blissey.hp = 200;
1722
+ blissey.stats.hp = 400;
1723
+ blissey.status = gen2.Status.init(.PAR);
1724
+ try log.drag(.{ p2.ident(3), &blissey });
1725
+ const par = 0b1000000;
1726
+ const expected = &(.{
1727
+ N(ArgType.Drag),
1728
+ 0b1011,
1729
+ N(S2.Blissey),
1730
+ N(gen2.Gender.Female),
1731
+ 91,
1732
+ } ++ switch (endian) {
1733
+ .big => .{ 0, 200, 1, 144, par },
1734
+ .little => .{ 200, 0, 144, 1, par },
1735
+ });
1736
+ try expectLog2(expected, buf[0..10]);
1737
+ stream.reset();
1738
+ }
1739
+
1740
+ test "|-item|" {
1741
+ try log.item(.{ p2.ident(2), I2.GoldBerry, p1.ident(4) });
1742
+ try expectLog2(&.{ N(ArgType.Item), 0b1010, N(I2.GoldBerry), 0b0100 }, buf[0..4]);
1743
+ stream.reset();
1744
+ }
1745
+
1746
+ test "|-enditem|" {
1747
+ try log.enditem(.{ p1.ident(1), I2.BerserkGene, .None });
1748
+ try expectLog2(&.{ N(ArgType.EndItem), 0b0001, N(I2.BerserkGene), N(EndItem.None) }, buf[0..4]);
1749
+ stream.reset();
1750
+
1751
+ try log.enditem(.{ p2.ident(4), I2.PRZCureBerry, .Eat });
1752
+ try expectLog2(&.{ N(ArgType.EndItem), 0b1100, N(I2.PRZCureBerry), N(EndItem.Eat) }, buf[0..4]);
1753
+ stream.reset();
1754
+ }
1755
+
1756
+ test "|-cureteam|" {
1757
+ try log.cureteam(.{p2.ident(5)});
1758
+ try expectLog2(&.{ N(ArgType.CureTeam), 0b1101 }, buf[0..2]);
1759
+ stream.reset();
1760
+ }
1761
+
1762
+ test "|-sethp|" {
1763
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
1764
+ blissey.hp = 612;
1765
+ try log.sethp(.{ p2.ident(2), &blissey, .Silent });
1766
+ const expected: []const u8 = switch (endian) {
1767
+ .big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1768
+ .little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1769
+ };
1770
+ try expectLog2(expected, buf[0..8]);
1771
+ stream.reset();
1772
+ }
1773
+
1774
+ test "|-setboost|" {
1775
+ try log.setboost(.{ p2.ident(6), 6 });
1776
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1110, 12 }, buf[0..3]);
1777
+ stream.reset();
1778
+
1779
+ try log.setboost(.{ p2.ident(3), 2 });
1780
+ try expectLog2(&.{ N(ArgType.SetBoost), 0b1011, 8 }, buf[0..3]);
1781
+ stream.reset();
1782
+ }
1783
+
1784
+ test "|-copyboost|" {
1785
+ try log.copyboost(.{ p2.ident(3), p1.ident(6) });
1786
+ try expectLog2(&.{ N(ArgType.CopyBoost), 0b1011, 0b0110 }, buf[0..3]);
1787
+ stream.reset();
1788
+ }
1789
+
1790
+ test "|-sidestart|" {
1791
+ try log.sidestart(.{ .P2, .Reflect });
1792
+ try expectLog2(&.{ N(ArgType.SideStart), 1, N(Side.Reflect) }, buf[0..3]);
1793
+ stream.reset();
1794
+ }
1795
+
1796
+ test "|-sideend|" {
1797
+ try log.sideend(.{ .P2, .LightScreen });
1798
+ try expectLog2(&.{ N(ArgType.SideEnd), 1, N(Side.LightScreen) }, buf[0..3]);
1799
+ stream.reset();
1800
+
1801
+ try log.sideend(.{ .P1, .Spikes, p1.ident(3) });
1802
+ try expectLog2(&.{
1803
+ N(ArgType.SideEnd),
1804
+ 0,
1805
+ N(Side.Spikes),
1806
+ 0b0011,
1807
+ }, buf[0..4]);
1808
+ stream.reset();
1809
+ }
1810
+
1811
+ test "|-singlemove|" {
1812
+ try log.singlemove(.{ p2.ident(2), M2.DestinyBond });
1813
+ try expectLog2(&.{ N(ArgType.SingleMove), 0b1010, N(M2.DestinyBond) }, buf[0..3]);
1814
+ stream.reset();
1815
+ }
1816
+
1817
+ test "|-singleturn|" {
1818
+ try log.singleturn(.{ p1.ident(3), M2.Protect });
1819
+ try expectLog2(&.{ N(ArgType.SingleTurn), 0b0011, N(M2.Protect) }, buf[0..3]);
1820
+ stream.reset();
1821
+ }
1822
+
1823
+ test "|-weather|" {
1824
+ try log.weather(.{ W2.Rain, .Upkeep });
1825
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.Rain), N(Weather.Upkeep) }, buf[0..3]);
1826
+ stream.reset();
1827
+
1828
+ try log.weather(.{ W2.None, .None });
1829
+ try expectLog2(&.{ N(ArgType.Weather), N(W2.None), N(Weather.None) }, buf[0..3]);
1830
+ stream.reset();
1831
+ }