@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.49f6bf26

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