@pkmn/engine 0.1.0-dev.ad6136e4 → 0.1.0-dev.b3f3e3a6

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