@pkmn/engine 0.1.0-dev.437f6a52 → 0.1.0-dev.44b2e6ec

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