@pkmn/engine 0.1.0-dev.44a51703 → 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 (53) hide show
  1. package/README.md +85 -51
  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 +3 -2
  8. package/build/pkg/data.js.map +1 -1
  9. package/build/pkg/gen1.js +15 -18
  10. package/build/pkg/gen1.js.map +1 -1
  11. package/build/pkg/index.d.ts +2 -1
  12. package/build/pkg/index.js.map +1 -1
  13. package/build/pkg/protocol.js +57 -38
  14. package/build/pkg/protocol.js.map +1 -1
  15. package/build/test/display.js.map +1 -1
  16. package/build/tools/debug.js +3 -1
  17. package/build/tools/debug.js.map +1 -1
  18. package/build.zig +115 -73
  19. package/package.json +43 -21
  20. package/src/bin/install-pkmn-engine +10 -5
  21. package/src/lib/binding/node.zig +42 -138
  22. package/src/lib/binding/wasm.zig +2 -0
  23. package/src/lib/common/battle.zig +17 -0
  24. package/src/lib/common/data.zig +8 -20
  25. package/src/lib/common/debug.zig +20 -11
  26. package/src/lib/common/js.zig +118 -0
  27. package/src/lib/common/optional.zig +67 -0
  28. package/src/lib/common/options.zig +9 -0
  29. package/src/lib/common/protocol.zig +835 -307
  30. package/src/lib/common/rational.zig +357 -0
  31. package/src/lib/common/rng.zig +19 -10
  32. package/src/lib/common/util.zig +12 -0
  33. package/src/lib/gen1/README.md +284 -204
  34. package/src/lib/gen1/calc.zig +426 -0
  35. package/src/lib/gen1/chance.zig +1060 -0
  36. package/src/lib/gen1/data/moves.zig +192 -180
  37. package/src/lib/gen1/data/species.zig +2 -2
  38. package/src/lib/gen1/data/types.zig +7 -7
  39. package/src/lib/gen1/data.zig +67 -58
  40. package/src/lib/gen1/helpers.zig +566 -10
  41. package/src/lib/gen1/mechanics.zig +897 -670
  42. package/src/lib/gen2/README.md +105 -85
  43. package/src/lib/gen2/calc.zig +143 -0
  44. package/src/lib/gen2/chance.zig +1314 -0
  45. package/src/lib/gen2/data/items.zig +12 -18
  46. package/src/lib/gen2/data/moves.zig +663 -569
  47. package/src/lib/gen2/data/species.zig +1 -1
  48. package/src/lib/gen2/data/types.zig +134 -31
  49. package/src/lib/gen2/data.zig +115 -78
  50. package/src/lib/gen2/generated.zig +806 -0
  51. package/src/lib/gen2/helpers.zig +11 -17
  52. package/src/lib/gen2/mechanics.zig +3506 -12
  53. 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
 
@@ -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,8 +289,34 @@ 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
+
314
+ /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
315
+ /// Ignores anything sent to it, though protocol logging should additionally be turned off
316
+ /// entirely with `options.log`.
222
317
  pub const NULL = Log(@TypeOf(std.io.null_writer)){ .writer = std.io.null_writer };
223
318
 
319
+ /// Logs protocol information to its `Writer` during a battle update when `options.log` is enabled.
224
320
  pub fn Log(comptime Writer: type) type {
225
321
  return struct {
226
322
  const Self = @This();
@@ -229,335 +325,497 @@ pub fn Log(comptime Writer: type) type {
229
325
 
230
326
  pub const Error = Writer.Error;
231
327
 
232
- 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 {
233
330
  if (!enabled) return;
234
- assert(m != .None);
331
+
332
+ assert(args[1] != .None);
235
333
  try self.writer.writeAll(&.{
236
334
  @intFromEnum(ArgType.Move),
237
- @as(u8, @bitCast(source)),
238
- @intFromEnum(m),
239
- @as(u8, @bitCast(target)),
240
- });
241
- const none = &[_]u8{@intFromEnum(Move.None)};
242
- try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
243
- .Null => none,
244
- .Optional => if (from) |f| blk: {
245
- assert(@intFromEnum(f) != 0);
246
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
247
- } else none,
248
- else => blk: {
249
- assert(@intFromEnum(from) != 0);
250
- break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(from) };
251
- },
335
+ @as(u8, @bitCast(args[0])),
336
+ @intFromEnum(args[1]),
337
+ @as(u8, @bitCast(args[2])),
252
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]) });
253
343
  }
254
344
 
255
- pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
256
- if (!enabled) return;
257
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
258
- try self.writer.writeAll(&.{ @intFromEnum(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});
345
+ // ident: ID, pokemon: <Pokemon>
346
+ pub fn switched(self: Self, args: anytype) Error!void {
347
+ return switchDrag(self, .Switch, args);
262
348
  }
263
349
 
264
- 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 {
265
352
  if (!enabled) return;
266
- assert(reason != .Disable);
267
- try self.writer.writeAll(&.{
268
- @intFromEnum(ArgType.Cant),
269
- @as(u8, @bitCast(ident)),
270
- @intFromEnum(reason),
271
- });
353
+
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});
272
367
  }
273
368
 
274
- 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 {
275
372
  if (!enabled) return;
373
+
374
+ assert(args[1] != .Disable or args.len == 3);
276
375
  try self.writer.writeAll(&.{
277
376
  @intFromEnum(ArgType.Cant),
278
- @as(u8, @bitCast(ident)),
279
- @intFromEnum(Cant.Disable),
280
- @intFromEnum(m),
377
+ @as(u8, @bitCast(args[0])),
378
+ @intFromEnum(@as(Cant, args[1])),
281
379
  });
380
+ if (args.len == 3) try self.writer.writeByte(@intFromEnum(args[2]));
282
381
  }
283
382
 
284
- 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 {
285
385
  if (!enabled) return;
286
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(ident)) });
287
- if (done) try self.writer.writeByte(@intFromEnum(ArgType.None));
386
+
387
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Faint), @as(u8, @bitCast(args[0])) });
388
+ if (args[1]) try self.writer.writeByte(@intFromEnum(ArgType.None));
288
389
  }
289
390
 
290
- pub fn turn(self: Self, num: u16) Error!void {
391
+ // num: u16
392
+ pub fn turn(self: Self, args: anytype) Error!void {
291
393
  if (!enabled) return;
394
+
292
395
  try self.writer.writeByte(@intFromEnum(ArgType.Turn));
293
- try self.writer.writeIntNative(u16, num);
396
+ try self.writer.writeInt(u16, args[0], endian);
294
397
  try self.writer.writeByte(@intFromEnum(ArgType.None));
295
398
  }
296
399
 
297
- pub fn win(self: Self, player: Player) Error!void {
400
+ // player: Player
401
+ pub fn win(self: Self, args: anytype) Error!void {
298
402
  if (!enabled) return;
403
+
299
404
  try self.writer.writeAll(&.{
300
405
  @intFromEnum(ArgType.Win),
301
- @intFromEnum(player),
406
+ @intFromEnum(@as(Player, args[0])),
302
407
  @intFromEnum(ArgType.None),
303
408
  });
304
409
  }
305
410
 
306
- pub fn tie(self: Self) Error!void {
411
+ // -
412
+ pub fn tie(self: Self, _: anytype) Error!void {
307
413
  if (!enabled) return;
414
+
308
415
  try self.writer.writeAll(&.{ @intFromEnum(ArgType.Tie), @intFromEnum(ArgType.None) });
309
416
  }
310
417
 
311
- 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 {
312
421
  if (!enabled) return;
313
- assert(@intFromEnum(reason) <= @intFromEnum(Damage.LeechSeed));
314
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(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, @intFromEnum(reason) });
318
- }
319
422
 
320
- pub fn damageOf(
321
- self: Self,
322
- ident: ID,
323
- pokemon: anytype,
324
- reason: Damage,
325
- source: ID,
326
- ) Error!void {
327
- if (!enabled) return;
328
- assert(@intFromEnum(reason) == @intFromEnum(Damage.RecoilOf));
329
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
330
- try self.writer.writeIntNative(u16, pokemon.hp);
331
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
423
+ assert(args[2] != .RecoilOf or args.len == 4);
332
424
  try self.writer.writeAll(&.{
333
- pokemon.status,
334
- @intFromEnum(reason),
335
- @as(u8, @bitCast(source)),
425
+ @intFromEnum(ArgType.Damage),
426
+ @as(u8, @bitCast(args[0])),
336
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])));
337
432
  }
338
433
 
339
- pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) 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
- assert(reason != .Drain);
342
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(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, @intFromEnum(reason) });
346
- }
347
438
 
348
- pub fn drain(self: Self, source: ID, pokemon: anytype, target: ID) Error!void {
349
- if (!enabled) return;
350
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(source)) });
351
- try self.writer.writeIntNative(u16, pokemon.hp);
352
- try self.writer.writeIntNative(u16, pokemon.stats.hp);
353
- try self.writer.writeAll(&.{
354
- pokemon.status,
355
- @intFromEnum(Heal.Drain),
356
- @as(u8, @bitCast(target)),
357
- });
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])));
358
445
  }
359
446
 
360
- pub fn status(self: Self, ident: ID, value: u8, reason: Status) 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 {
361
450
  if (!enabled) return;
362
- assert(reason != .From);
363
- try self.writer.writeAll(&.{
364
- @intFromEnum(ArgType.Status),
365
- @as(u8, @bitCast(ident)),
366
- value,
367
- @intFromEnum(reason),
368
- });
369
- }
370
451
 
371
- pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
372
- if (!enabled) return;
452
+ assert(args[2] != .From or args.len == 4);
373
453
  try self.writer.writeAll(&.{
374
454
  @intFromEnum(ArgType.Status),
375
- @as(u8, @bitCast(ident)),
376
- value,
377
- @intFromEnum(Status.From),
378
- @intFromEnum(m),
455
+ @as(u8, @bitCast(args[0])),
456
+ args[1],
457
+ @intFromEnum(@as(Status, args[2])),
379
458
  });
459
+ if (args.len == 4) try self.writer.writeByte(@intFromEnum(args[3]));
380
460
  }
381
461
 
382
- pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
462
+ // ident: ID, value: u8, reason: CureStatus
463
+ pub fn curestatus(self: Self, args: anytype) Error!void {
383
464
  if (!enabled) return;
465
+
384
466
  try self.writer.writeAll(&.{
385
467
  @intFromEnum(ArgType.CureStatus),
386
- @as(u8, @bitCast(ident)),
387
- value,
388
- @intFromEnum(reason),
468
+ @as(u8, @bitCast(args[0])),
469
+ args[1],
470
+ @intFromEnum(@as(CureStatus, args[2])),
389
471
  });
390
472
  }
391
473
 
392
- pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
474
+ // ident: ID, reason: Boost, num: i8
475
+ pub fn boost(self: Self, args: anytype) Error!void {
393
476
  if (!enabled) return;
394
- assert(num != 0 and (reason != .Rage or num > 0));
477
+
478
+ assert(args[2] != 0 and (args[1] != .Rage or args[2] > 0));
395
479
  try self.writer.writeAll(&.{
396
480
  @intFromEnum(ArgType.Boost),
397
- @as(u8, @bitCast(ident)),
398
- @intFromEnum(reason),
399
- @as(u8, @intCast(num + 6)),
481
+ @as(u8, @bitCast(args[0])),
482
+ @intFromEnum(@as(Boost, args[1])),
483
+ @as(u8, @intCast(@as(i8, args[2]) + 6)),
400
484
  });
401
485
  }
402
486
 
403
- pub fn clearallboost(self: Self) Error!void {
487
+ // -
488
+ pub fn clearallboost(self: Self, _: anytype) Error!void {
404
489
  if (!enabled) return;
490
+
405
491
  try self.writer.writeAll(&.{@intFromEnum(ArgType.ClearAllBoost)});
406
492
  }
407
493
 
408
- pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
494
+ // ident: ID, reason: Fail
495
+ pub fn fail(self: Self, args: anytype) Error!void {
409
496
  if (!enabled) return;
497
+
410
498
  try self.writer.writeAll(&.{
411
499
  @intFromEnum(ArgType.Fail),
412
- @as(u8, @bitCast(ident)),
413
- @intFromEnum(reason),
500
+ @as(u8, @bitCast(args[0])),
501
+ @intFromEnum(@as(Fail, args[1])),
414
502
  });
415
503
  }
416
504
 
417
- pub fn miss(self: Self, source: ID) Error!void {
505
+ // source: ID
506
+ pub fn miss(self: Self, args: anytype) Error!void {
418
507
  if (!enabled) return;
419
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(source)) });
508
+
509
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(args[0])) });
420
510
  }
421
511
 
422
- pub fn hitcount(self: Self, ident: ID, num: u8) Error!void {
512
+ // ident: ID, num: u8
513
+ pub fn hitcount(self: Self, args: anytype) Error!void {
423
514
  if (!enabled) return;
515
+
424
516
  try self.writer.writeAll(&.{
425
517
  @intFromEnum(ArgType.HitCount),
426
- @as(u8, @bitCast(ident)),
427
- num,
518
+ @as(u8, @bitCast(args[0])),
519
+ args[1],
428
520
  });
429
521
  }
430
522
 
431
- pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
523
+ // source: ID, m: <Move>
524
+ pub fn prepare(self: Self, args: anytype) Error!void {
432
525
  if (!enabled) return;
526
+
433
527
  try self.writer.writeAll(&.{
434
528
  @intFromEnum(ArgType.Prepare),
435
- @as(u8, @bitCast(source)),
436
- @intFromEnum(m),
529
+ @as(u8, @bitCast(args[0])),
530
+ @intFromEnum(args[1]),
437
531
  });
438
532
  }
439
533
 
440
- pub fn mustrecharge(self: Self, ident: ID) Error!void {
534
+ // ident: ID
535
+ pub fn mustrecharge(self: Self, args: anytype) Error!void {
441
536
  if (!enabled) return;
537
+
442
538
  try self.writer.writeAll(&.{
443
539
  @intFromEnum(ArgType.MustRecharge),
444
- @as(u8, @bitCast(ident)),
540
+ @as(u8, @bitCast(args[0])),
445
541
  });
446
542
  }
447
543
 
448
- pub fn activate(self: Self, ident: ID, reason: Activate) 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 {
449
548
  if (!enabled) return;
549
+
550
+ assert(args.len <= 4);
551
+ const reason: Activate = args[1];
450
552
  try self.writer.writeAll(&.{
451
553
  @intFromEnum(ArgType.Activate),
452
- @as(u8, @bitCast(ident)),
554
+ @as(u8, @bitCast(args[0])),
453
555
  @intFromEnum(reason),
454
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
+ }
455
565
  }
456
566
 
457
- pub fn fieldactivate(self: Self) Error!void {
567
+ // -
568
+ pub fn fieldactivate(self: Self, _: anytype) Error!void {
458
569
  if (!enabled) return;
570
+
459
571
  try self.writer.writeAll(&.{@intFromEnum(ArgType.FieldActivate)});
460
572
  }
461
573
 
462
- pub fn start(self: Self, ident: ID, reason: Start) 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 {
463
578
  if (!enabled) return;
464
- assert(@intFromEnum(reason) < @intFromEnum(Start.TypeChange));
579
+
580
+ assert(args.len <= 4);
581
+ const reason: Start = args[1];
465
582
  try self.writer.writeAll(&.{
466
583
  @intFromEnum(ArgType.Start),
467
- @as(u8, @bitCast(ident)),
584
+ @as(u8, @bitCast(args[0])),
468
585
  @intFromEnum(reason),
469
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
+ }
470
598
  }
471
599
 
472
- pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
600
+ // ident: ID, reason: End
601
+ pub fn end(self: Self, args: anytype) Error!void {
473
602
  if (!enabled) return;
603
+
474
604
  try self.writer.writeAll(&.{
475
- @intFromEnum(ArgType.Start),
476
- @as(u8, @bitCast(source)),
477
- @intFromEnum(Start.TypeChange),
478
- @as(u8, @bitCast(types)),
479
- @as(u8, @bitCast(target)),
605
+ @intFromEnum(ArgType.End),
606
+ @as(u8, @bitCast(args[0])),
607
+ @intFromEnum(@as(End, args[1])),
480
608
  });
481
609
  }
482
610
 
483
- pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) 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 {
484
620
  if (!enabled) return;
485
- assert(@intFromEnum(reason) > @intFromEnum(Start.TypeChange));
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 {
627
+ if (!enabled) return;
628
+
486
629
  try self.writer.writeAll(&.{
487
- @intFromEnum(ArgType.Start),
488
- @as(u8, @bitCast(ident)),
489
- @intFromEnum(reason),
490
- @intFromEnum(m),
630
+ @intFromEnum(ArgType.SuperEffective),
631
+ @as(u8, @bitCast(args[0])),
491
632
  });
492
633
  }
493
634
 
494
- pub fn end(self: Self, ident: ID, reason: End) Error!void {
635
+ // ident: ID
636
+ pub fn resisted(self: Self, args: anytype) Error!void {
495
637
  if (!enabled) return;
638
+
496
639
  try self.writer.writeAll(&.{
497
- @intFromEnum(ArgType.End),
498
- @as(u8, @bitCast(ident)),
499
- @intFromEnum(reason),
640
+ @intFromEnum(ArgType.Resisted),
641
+ @as(u8, @bitCast(args[0])),
500
642
  });
501
643
  }
502
644
 
503
- pub fn ohko(self: Self) Error!void {
645
+ // ident: ID, reason: Immune
646
+ pub fn immune(self: Self, args: anytype) Error!void {
504
647
  if (!enabled) return;
505
- try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
648
+
649
+ try self.writer.writeAll(&.{
650
+ @intFromEnum(ArgType.Immune),
651
+ @as(u8, @bitCast(args[0])),
652
+ @intFromEnum(@as(Immune, args[1])),
653
+ });
506
654
  }
507
655
 
508
- pub fn crit(self: Self, ident: ID) Error!void {
656
+ // source: ID, target: ID
657
+ pub fn transform(self: Self, args: anytype) Error!void {
509
658
  if (!enabled) return;
510
- try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(ident)) });
659
+
660
+ try self.writer.writeAll(&.{
661
+ @intFromEnum(ArgType.Transform),
662
+ @as(u8, @bitCast(args[0])),
663
+ @as(u8, @bitCast(args[1])),
664
+ });
511
665
  }
512
666
 
513
- pub fn supereffective(self: Self, ident: 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 {
514
674
  if (!enabled) return;
675
+
515
676
  try self.writer.writeAll(&.{
516
- @intFromEnum(ArgType.SuperEffective),
517
- @as(u8, @bitCast(ident)),
677
+ @intFromEnum(ArgType.Item),
678
+ @as(u8, @bitCast(args[0])),
679
+ @intFromEnum(args[1]),
680
+ @as(u8, @bitCast(args[2])),
518
681
  });
519
682
  }
520
683
 
521
- pub fn resisted(self: Self, ident: ID) Error!void {
684
+ // ident: ID, item: <Item>, reason: EndItem
685
+ pub fn enditem(self: Self, args: anytype) Error!void {
522
686
  if (!enabled) return;
687
+
523
688
  try self.writer.writeAll(&.{
524
- @intFromEnum(ArgType.Resisted),
525
- @as(u8, @bitCast(ident)),
689
+ @intFromEnum(ArgType.EndItem),
690
+ @as(u8, @bitCast(args[0])),
691
+ @intFromEnum(args[1]),
692
+ @intFromEnum(@as(EndItem, args[2])),
526
693
  });
527
694
  }
528
695
 
529
- pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
696
+ // source: ID
697
+ pub fn cureteam(self: Self, args: anytype) Error!void {
530
698
  if (!enabled) return;
699
+
531
700
  try self.writer.writeAll(&.{
532
- @intFromEnum(ArgType.Immune),
533
- @as(u8, @bitCast(ident)),
534
- @intFromEnum(reason),
701
+ @intFromEnum(ArgType.CureTeam),
702
+ @as(u8, @bitCast(args[0])),
535
703
  });
536
704
  }
537
705
 
538
- pub fn transform(self: Self, source: ID, target: ID) Error!void {
706
+ // ident: ID, pokemon: <Pokemon>, reason: SetHP
707
+ pub fn sethp(self: Self, args: anytype) Error!void {
539
708
  if (!enabled) return;
709
+
710
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(args[0])) });
711
+ try self.writer.writeInt(u16, args[1].hp, endian);
712
+ try self.writer.writeInt(u16, args[1].stats.hp, endian);
713
+ try self.writer.writeAll(&.{args[1].status});
714
+ try self.writer.writeByte(@intFromEnum(@as(SetHP, args[2])));
715
+ }
716
+
717
+ // ident: ID, num: i8
718
+ pub fn setboost(self: Self, args: anytype) Error!void {
719
+ if (!enabled) return;
720
+
540
721
  try self.writer.writeAll(&.{
541
- @intFromEnum(ArgType.Transform),
542
- @as(u8, @bitCast(source)),
543
- @as(u8, @bitCast(target)),
722
+ @intFromEnum(ArgType.SetBoost),
723
+ @as(u8, @bitCast(args[0])),
724
+ @as(u8, @intCast(args[1] + 6)),
725
+ });
726
+ }
727
+
728
+ // source: ID, target: ID
729
+ pub fn copyboost(self: Self, args: anytype) Error!void {
730
+ if (!enabled) return;
731
+
732
+ try self.writer.writeAll(&.{
733
+ @intFromEnum(ArgType.CopyBoost),
734
+ @as(u8, @bitCast(args[0])),
735
+ @as(u8, @bitCast(args[1])),
544
736
  });
545
737
  }
546
738
 
547
- pub fn laststill(self: Self) Error!void {
739
+ // player: Player, reason: Side
740
+ pub fn sidestart(self: Self, args: anytype) Error!void {
548
741
  if (!enabled) return;
742
+
743
+ try self.writer.writeAll(&.{
744
+ @intFromEnum(ArgType.SideStart),
745
+ @intFromEnum(@as(Player, args[0])),
746
+ @intFromEnum(@as(Side, args[1])),
747
+ });
748
+ }
749
+
750
+ // player: Player, reason: Side
751
+ // player: Player, reason: .Spikes, source: ID
752
+ pub fn sideend(self: Self, args: anytype) Error!void {
753
+ if (!enabled) return;
754
+
755
+ assert(args[1] != .Spikes or args.len == 3);
756
+ try self.writer.writeAll(&.{
757
+ @intFromEnum(ArgType.SideEnd),
758
+ @intFromEnum(@as(Player, args[0])),
759
+ @intFromEnum(@as(Side, args[1])),
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);
772
+ }
773
+
774
+ // ident: ID, move: <Move>
775
+ fn single(self: Self, arg: ArgType, args: anytype) Error!void {
776
+ if (!enabled) return;
777
+
778
+ try self.writer.writeAll(&.{
779
+ @intFromEnum(arg),
780
+ @as(u8, @bitCast(args[0])),
781
+ @intFromEnum(args[1]),
782
+ });
783
+ }
784
+
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 {
799
+ if (!enabled) return;
800
+
549
801
  try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
550
802
  }
551
803
 
552
- pub fn lastmiss(self: Self) Error!void {
804
+ // -
805
+ pub fn lastmiss(self: Self, _: anytype) Error!void {
553
806
  if (!enabled) return;
807
+
554
808
  try self.writer.writeAll(&.{@intFromEnum(ArgType.LastMiss)});
555
809
  }
556
810
  };
557
811
  }
558
812
 
813
+ /// `Log` type backed by the optimized `ByteStream.Writer`.
559
814
  pub const FixedLog = Log(ByteStream.Writer);
560
815
 
816
+ /// Stripped down version of `std.io.FixedBufferStream` optimized for efficiently writing the
817
+ /// individual protocol bytes. Note that the `ByteStream.Writer` is **not** a `std.io.Writer` and
818
+ /// should not be used for general purpose writing.
561
819
  pub const ByteStream = struct {
562
820
  buffer: []u8,
563
821
  pos: usize = 0,
@@ -575,10 +833,10 @@ pub const ByteStream = struct {
575
833
  try self.stream.writeByte(byte);
576
834
  }
577
835
 
578
- 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 {
579
837
  // TODO: rework this to write directly to the buffer?
580
- var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
581
- 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);
582
840
  return self.writeAll(&bytes);
583
841
  }
584
842
  };
@@ -598,10 +856,14 @@ pub const ByteStream = struct {
598
856
  }
599
857
  };
600
858
 
601
- pub const Kind = enum { Move, Species, Type, Status };
602
- pub const Formatter = fn (Kind, u8) []const u8;
859
+ pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
603
860
 
604
- 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 {
605
867
  print("\n", .{});
606
868
 
607
869
  var i: usize = 0;
@@ -639,6 +901,18 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
639
901
  .Resisted => "|-resisted|",
640
902
  .Immune => "|-immune|",
641
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|",
642
916
  else => unreachable,
643
917
  };
644
918
  printc("{s}", .{name}, a, b, &i, 1, color);
@@ -654,33 +928,43 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
654
928
  .Move => {
655
929
  const source = ID.from(@intCast(a[i]));
656
930
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
657
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
931
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
658
932
  const target = ID.from(@intCast(a[i]));
659
933
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
660
934
  const reason: Move = @enumFromInt(a[i]);
661
935
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
662
- 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
+ }
663
939
  },
664
- .Switch => {
940
+ .Switch, .Drag => {
665
941
  const id = ID.from(@intCast(a[i]));
666
942
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
667
- 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
+ }
668
952
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
669
953
  switch (endian) {
670
- .Big => {
954
+ .big => {
671
955
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
672
956
  printc(" {d}", .{hp}, a, b, &i, 2, color);
673
957
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
674
958
  printc("/{d}", .{hp}, a, b, &i, 2, color);
675
959
  },
676
- .Little => {
960
+ .little => {
677
961
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
678
962
  printc(" {d}", .{hp}, a, b, &i, 2, color);
679
963
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
680
964
  printc("/{d}", .{hp}, a, b, &i, 2, color);
681
965
  },
682
966
  }
683
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
967
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
684
968
  },
685
969
  .Cant => {
686
970
  const id = ID.from(@intCast(a[i]));
@@ -688,7 +972,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
688
972
  const reason: Cant = @enumFromInt(a[i]);
689
973
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
690
974
  if (reason == .Disable) {
691
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
975
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
692
976
  }
693
977
  },
694
978
  .Faint,
@@ -697,14 +981,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
697
981
  .Crit,
698
982
  .SuperEffective,
699
983
  .Resisted,
984
+ .CureTeam,
700
985
  => {
701
986
  const id = ID.from(@intCast(a[i]));
702
987
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
703
988
  },
704
989
  .Turn => {
705
990
  const turn = switch (endian) {
706
- .Big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
707
- .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]),
708
993
  };
709
994
  printc(" {d}", .{turn}, a, b, &i, 2, color);
710
995
  },
@@ -715,20 +1000,20 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
715
1000
  var id = ID.from(@intCast(a[i]));
716
1001
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
717
1002
  switch (endian) {
718
- .Big => {
1003
+ .big => {
719
1004
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
720
1005
  printc(" {d}", .{hp}, a, b, &i, 2, color);
721
1006
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
722
1007
  printc("/{d}", .{hp}, a, b, &i, 2, color);
723
1008
  },
724
- .Little => {
1009
+ .little => {
725
1010
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
726
1011
  printc(" {d}", .{hp}, a, b, &i, 2, color);
727
1012
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
728
1013
  printc("/{d}", .{hp}, a, b, &i, 2, color);
729
1014
  },
730
1015
  }
731
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1016
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
732
1017
  if (arg == .Damage) {
733
1018
  const reason: Damage = @enumFromInt(a[i]);
734
1019
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
@@ -748,15 +1033,17 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
748
1033
  .Status => {
749
1034
  const id = ID.from(@intCast(a[i]));
750
1035
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
751
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1036
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
752
1037
  const reason: Status = @enumFromInt(a[i]);
753
1038
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
754
- 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
+ }
755
1042
  },
756
1043
  .CureStatus => {
757
1044
  const id = ID.from(@intCast(a[i]));
758
1045
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
759
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
1046
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
760
1047
  const reason: CureStatus = @enumFromInt(a[i]);
761
1048
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
762
1049
  },
@@ -771,20 +1058,26 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
771
1058
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
772
1059
  printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
773
1060
  },
774
- .HitCount => {
1061
+ .HitCount, .SetBoost => {
775
1062
  const id = ID.from(@intCast(a[i]));
776
1063
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
777
1064
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
778
1065
  },
779
- .Prepare => {
1066
+ .Prepare, .SingleMove, .SingleTurn => {
780
1067
  const id = ID.from(@intCast(a[i]));
781
1068
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
782
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1069
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
783
1070
  },
784
1071
  .Activate => {
785
1072
  const id = ID.from(@intCast(a[i]));
786
1073
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
787
- 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
+ }
788
1081
  },
789
1082
  .Start => {
790
1083
  const id = ID.from(@intCast(a[i]));
@@ -792,14 +1085,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
792
1085
  const reason = a[i];
793
1086
  printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
794
1087
  if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
1088
+ // FIXME
795
1089
  const types = @as(gen1.Types, @bitCast(a[i]));
796
1090
  const args = .{
797
- formatter(.Type, @intFromEnum(types.type1)),
798
- formatter(.Type, @intFromEnum(types.type2)),
1091
+ formatter(gen, .Type, @intFromEnum(types.type1)),
1092
+ formatter(gen, .Type, @intFromEnum(types.type2)),
799
1093
  };
800
1094
  printc(" {s}/{s}", args, a, b, &i, 1, color);
801
1095
  } else if (reason >= @intFromEnum(Start.Disable)) {
802
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
1096
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
803
1097
  }
804
1098
  },
805
1099
  .End => {
@@ -812,12 +1106,64 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
812
1106
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
813
1107
  printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
814
1108
  },
815
- .Transform => {
1109
+ .Transform, .CopyBoost => {
816
1110
  const source = ID.from(@intCast(a[i]));
817
1111
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
818
1112
  const target = ID.from(@intCast(a[i]));
819
1113
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
820
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
+ },
821
1167
  else => unreachable,
822
1168
  }
823
1169
  print("\n", .{});
@@ -849,12 +1195,13 @@ fn printc(
849
1195
  }
850
1196
 
851
1197
  pub fn expectLog(
852
- comptime formatter: Formatter,
1198
+ comptime gen: anytype,
853
1199
  expected: []const u8,
854
1200
  actual: []const u8,
855
1201
  offset: usize,
856
1202
  ) !void {
857
1203
  if (!enabled) return;
1204
+
858
1205
  const color = color: {
859
1206
  if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
860
1207
  break :color true;
@@ -867,8 +1214,8 @@ pub fn expectLog(
867
1214
 
868
1215
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
869
1216
  error.TestExpectedEqual => {
870
- format(formatter, expected, null, color);
871
- format(formatter, actual, expected, color);
1217
+ format(gen, expected, null, color);
1218
+ format(gen, actual, expected, color);
872
1219
  return err;
873
1220
  },
874
1221
  else => return err,
@@ -894,8 +1241,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
894
1241
  }
895
1242
  }
896
1243
 
897
- const endian = builtin.target.cpu.arch.endian();
898
-
899
1244
  fn N(e: anytype) u8 {
900
1245
  return @intFromEnum(e);
901
1246
  }
@@ -908,49 +1253,70 @@ const gen1 = struct {
908
1253
  pub const helpers = @import("../gen1/helpers.zig");
909
1254
  };
910
1255
 
911
- var buf: [gen1.LOGS_SIZE]u8 = undefined;
912
- 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 };
913
1263
  var log: FixedLog = .{ .writer = stream.writer() };
914
1264
 
915
- const M = gen1.Move;
916
- 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;
917
1272
 
918
1273
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
919
- return expectLog(gen1Formatter, expected, actual, 0);
1274
+ return expectLog(gen1, expected, actual, 0);
920
1275
  }
921
1276
 
922
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1277
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1278
+ return expectLog(gen2, expected, actual, 0);
1279
+ }
1280
+
1281
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
923
1282
  return switch (kind) {
924
- .Move => @tagName(@as(M, @enumFromInt(byte))),
925
- .Species => @tagName(@as(S, @enumFromInt(byte))),
926
- .Type => @tagName(@as(gen1.Type, @enumFromInt(byte))),
927
- .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,
928
1295
  };
929
1296
  }
930
1297
 
931
1298
  test "|move|" {
932
- try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
1299
+ try log.move(.{ p2.ident(4), M1.Thunderbolt, p1.ident(5) });
933
1300
  try expectLog1(
934
- &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
1301
+ &.{ N(ArgType.Move), 0b1100, N(M1.Thunderbolt), 0b0101, N(Move.None) },
935
1302
  buf[0..5],
936
1303
  );
937
1304
  stream.reset();
938
1305
 
939
- try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
940
- const wrap = N(M.Wrap);
1306
+ try log.move(.{ p2.ident(4), M1.Pound, p1.ident(5), M1.Metronome });
941
1307
  try expectLog1(
942
- &.{ 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) },
943
1309
  buf[0..6],
944
1310
  );
945
1311
  stream.reset();
946
1312
 
947
- try log.move(p2.ident(4), M.SkullBash, .{}, null);
948
- try log.laststill();
1313
+ try log.move(.{ p2.ident(4), M1.SkullBash, ID{} });
1314
+ try log.laststill(.{});
949
1315
  try expectLog1(
950
1316
  &.{
951
1317
  N(ArgType.Move),
952
1318
  0b1100,
953
- N(M.SkullBash),
1319
+ N(M1.SkullBash),
954
1320
  0,
955
1321
  N(Move.None),
956
1322
  N(ArgType.LastStill),
@@ -959,13 +1325,13 @@ test "|move|" {
959
1325
  );
960
1326
  stream.reset();
961
1327
 
962
- try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
963
- try log.lastmiss();
1328
+ try log.move(.{ p2.ident(4), M1.Tackle, p1.ident(5) });
1329
+ try log.lastmiss(.{});
964
1330
  try expectLog1(
965
1331
  &.{
966
1332
  N(ArgType.Move),
967
1333
  0b1100,
968
- N(M.Tackle),
1334
+ N(M1.Tackle),
969
1335
  0b0101,
970
1336
  N(Move.None),
971
1337
  N(ArgType.LastMiss),
@@ -981,11 +1347,11 @@ test "|switch|" {
981
1347
  snorlax.hp = 200;
982
1348
  snorlax.stats.hp = 400;
983
1349
  snorlax.status = gen1.Status.init(.PAR);
984
- try log.switched(p2.ident(3), &snorlax);
1350
+ try log.switched(.{ p2.ident(3), &snorlax });
985
1351
  const par = 0b1000000;
986
1352
  var expected: []const u8 = switch (endian) {
987
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
988
- .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 },
989
1355
  };
990
1356
  try expectLog1(expected, buf[0..9]);
991
1357
  stream.reset();
@@ -993,62 +1359,81 @@ test "|switch|" {
993
1359
  snorlax.level = 100;
994
1360
  snorlax.hp = 0;
995
1361
  snorlax.status = 0;
996
- try log.switched(p2.ident(3), &snorlax);
1362
+ try log.switched(.{ p2.ident(3), &snorlax });
997
1363
  expected = switch (endian) {
998
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
999
- .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 },
1000
1366
  };
1001
1367
  try expectLog1(expected, buf[0..9]);
1002
1368
  stream.reset();
1003
1369
 
1004
1370
  snorlax.hp = 400;
1005
- try log.switched(p2.ident(3), &snorlax);
1371
+ try log.switched(.{ p2.ident(3), &snorlax });
1006
1372
  expected = switch (endian) {
1007
- .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
1008
- .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 },
1009
1375
  };
1010
1376
  try expectLog1(expected, buf[0..9]);
1011
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();
1012
1397
  }
1013
1398
 
1014
1399
  test "|cant|" {
1015
- try log.cant(p2.ident(6), .Bound);
1400
+ try log.cant(.{ p2.ident(6), .Bound });
1016
1401
  try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1017
1402
  stream.reset();
1018
1403
 
1019
- try log.disabled(p1.ident(2), M.Earthquake);
1020
- 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]);
1021
1406
  stream.reset();
1022
1407
  }
1023
1408
 
1024
1409
  test "|faint|" {
1025
- try log.faint(p2.ident(2), false);
1410
+ try log.faint(.{ p2.ident(2), false });
1026
1411
  try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1027
1412
  stream.reset();
1028
1413
 
1029
- try log.faint(p2.ident(2), true);
1414
+ try log.faint(.{ p2.ident(2), true });
1030
1415
  try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1031
1416
  stream.reset();
1032
1417
  }
1033
1418
 
1034
1419
  test "|turn|" {
1035
- try log.turn(42);
1036
- var expected = switch (endian) {
1037
- .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1038
- .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) },
1039
1424
  };
1040
1425
  try expectLog1(expected, buf[0..4]);
1041
1426
  stream.reset();
1042
1427
  }
1043
1428
 
1044
1429
  test "|win|" {
1045
- try log.win(.P2);
1430
+ try log.win(.{.P2});
1046
1431
  try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1047
1432
  stream.reset();
1048
1433
  }
1049
1434
 
1050
1435
  test "|tie|" {
1051
- try log.tie();
1436
+ try log.tie(.{});
1052
1437
  try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1053
1438
  stream.reset();
1054
1439
  }
@@ -1057,10 +1442,10 @@ test "|-damage|" {
1057
1442
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1058
1443
  chansey.hp = 612;
1059
1444
  chansey.status = gen1.Status.slp(1);
1060
- try log.damage(p2.ident(2), &chansey, .None);
1445
+ try log.damage(.{ p2.ident(2), &chansey, .None });
1061
1446
  var expected: []const u8 = switch (endian) {
1062
- .Big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1063
- .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) },
1064
1449
  };
1065
1450
  try expectLog1(expected, buf[0..8]);
1066
1451
  stream.reset();
@@ -1068,19 +1453,19 @@ test "|-damage|" {
1068
1453
  chansey.hp = 100;
1069
1454
  chansey.stats.hp = 256;
1070
1455
  chansey.status = 0;
1071
- try log.damage(p2.ident(2), &chansey, .Confusion);
1456
+ try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1072
1457
  expected = switch (endian) {
1073
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1074
- .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) },
1075
1460
  };
1076
1461
  try expectLog1(expected, buf[0..8]);
1077
1462
  stream.reset();
1078
1463
 
1079
1464
  chansey.status = gen1.Status.init(.PSN);
1080
- try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1465
+ try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1081
1466
  expected = switch (endian) {
1082
- .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1083
- .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 },
1084
1469
  };
1085
1470
  try expectLog1(expected, buf[0..9]);
1086
1471
  stream.reset();
@@ -1090,10 +1475,10 @@ test "|-heal|" {
1090
1475
  var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1091
1476
  chansey.hp = 612;
1092
1477
  chansey.status = gen1.Status.slp(1);
1093
- try log.heal(p2.ident(2), &chansey, .None);
1478
+ try log.heal(.{ p2.ident(2), &chansey, .None });
1094
1479
  var expected: []const u8 = switch (endian) {
1095
- .Big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1096
- .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) },
1097
1482
  };
1098
1483
  try expectLog1(expected, buf[0..8]);
1099
1484
  stream.reset();
@@ -1101,46 +1486,46 @@ test "|-heal|" {
1101
1486
  chansey.hp = 100;
1102
1487
  chansey.stats.hp = 256;
1103
1488
  chansey.status = 0;
1104
- try log.heal(p2.ident(2), &chansey, .Silent);
1489
+ try log.heal(.{ p2.ident(2), &chansey, .Silent });
1105
1490
  expected = switch (endian) {
1106
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1107
- .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) },
1108
1493
  };
1109
1494
  try expectLog1(expected, buf[0..8]);
1110
1495
  stream.reset();
1111
1496
 
1112
- try log.drain(p2.ident(2), &chansey, p1.ident(1));
1497
+ try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1113
1498
  expected = switch (endian) {
1114
- .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1115
- .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 },
1116
1501
  };
1117
1502
  try expectLog1(expected, buf[0..9]);
1118
1503
  stream.reset();
1119
1504
  }
1120
1505
 
1121
1506
  test "|-status|" {
1122
- try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1507
+ try log.status(.{ p2.ident(6), gen1.Status.init(.BRN), .None });
1123
1508
  try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1124
1509
  stream.reset();
1125
1510
 
1126
- try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1511
+ try log.status(.{ p2.ident(2), gen1.Status.init(.PSN), .Silent });
1127
1512
  try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1128
1513
  stream.reset();
1129
1514
 
1130
- 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 });
1131
1516
  try expectLog1(
1132
- &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1517
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M1.BodySlam) },
1133
1518
  buf[0..5],
1134
1519
  );
1135
1520
  stream.reset();
1136
1521
  }
1137
1522
 
1138
1523
  test "|-curestatus|" {
1139
- try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1524
+ try log.curestatus(.{ p2.ident(6), gen1.Status.slp(7), .Message });
1140
1525
  try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1141
1526
  stream.reset();
1142
1527
 
1143
- try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1528
+ try log.curestatus(.{ p1.ident(2), gen1.Status.TOX, .Silent });
1144
1529
  try expectLog1(&.{
1145
1530
  N(ArgType.CureStatus),
1146
1531
  0b0010,
@@ -1151,163 +1536,306 @@ test "|-curestatus|" {
1151
1536
  }
1152
1537
 
1153
1538
  test "|-boost|" {
1154
- try log.boost(p2.ident(6), .Speed, 2);
1539
+ try log.boost(.{ p2.ident(6), .Speed, 2 });
1155
1540
  try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1156
1541
  stream.reset();
1157
1542
 
1158
- try log.boost(p1.ident(2), .Rage, 1);
1543
+ try log.boost(.{ p1.ident(2), .Rage, 1 });
1159
1544
  try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1160
1545
  stream.reset();
1161
1546
 
1162
- try log.boost(p2.ident(3), .Defense, -2);
1547
+ try log.boost(.{ p2.ident(3), .Defense, -2 });
1163
1548
  try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1164
1549
  stream.reset();
1165
1550
  }
1166
1551
 
1167
1552
  test "|-clearallboost|" {
1168
- try log.clearallboost();
1553
+ try log.clearallboost(.{});
1169
1554
  try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1170
1555
  stream.reset();
1171
1556
  }
1172
1557
 
1173
1558
  test "|-fail|" {
1174
- try log.fail(p2.ident(6), .None);
1559
+ try log.fail(.{ p2.ident(6), .None });
1175
1560
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1176
1561
  stream.reset();
1177
1562
 
1178
- try log.fail(p2.ident(6), .Sleep);
1563
+ try log.fail(.{ p2.ident(6), .Sleep });
1179
1564
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1180
1565
  stream.reset();
1181
1566
 
1182
- try log.fail(p2.ident(6), .Substitute);
1567
+ try log.fail(.{ p2.ident(6), .Substitute });
1183
1568
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1184
1569
  stream.reset();
1185
1570
 
1186
- try log.fail(p2.ident(6), .Weak);
1571
+ try log.fail(.{ p2.ident(6), .Weak });
1187
1572
  try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1188
1573
  stream.reset();
1189
1574
  }
1190
1575
 
1191
1576
  test "|-miss|" {
1192
- try log.miss(p2.ident(4));
1577
+ try log.miss(.{p2.ident(4)});
1193
1578
  try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1194
1579
  stream.reset();
1195
1580
  }
1196
1581
  test "|-hitcount|" {
1197
- try log.hitcount(p2.ident(1), 5);
1582
+ try log.hitcount(.{ p2.ident(1), 5 });
1198
1583
  try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1199
1584
  stream.reset();
1200
1585
  }
1201
1586
 
1202
1587
  test "|-prepare|" {
1203
- try log.prepare(p2.ident(2), M.Dig);
1204
- 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]);
1205
1590
  stream.reset();
1206
1591
  }
1207
1592
 
1208
1593
  test "|-mustrecharge|" {
1209
- try log.mustrecharge(p1.ident(6));
1594
+ try log.mustrecharge(.{p1.ident(6)});
1210
1595
  try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1211
1596
  stream.reset();
1212
1597
  }
1213
1598
 
1214
1599
  test "|-activate|" {
1215
- try log.activate(p1.ident(2), .Struggle);
1600
+ try log.activate(.{ p1.ident(2), .Struggle });
1216
1601
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1217
1602
  stream.reset();
1218
1603
 
1219
- try log.activate(p2.ident(4), .Mist);
1604
+ try log.activate(.{ p2.ident(4), .Mist });
1220
1605
  try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1221
1606
  stream.reset();
1222
1607
 
1223
- try log.activate(p2.ident(6), .Substitute);
1608
+ try log.activate(.{ p2.ident(6), .Substitute });
1224
1609
  try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1225
1610
  stream.reset();
1226
1611
 
1227
- try log.activate(p1.ident(2), .Splash);
1612
+ try log.activate(.{ p1.ident(2), .Splash });
1228
1613
  try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1229
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();
1230
1628
  }
1231
1629
 
1232
1630
  test "|-fieldactivate|" {
1233
- try log.fieldactivate();
1631
+ try log.fieldactivate(.{});
1234
1632
  try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1235
1633
  stream.reset();
1236
1634
  }
1237
1635
 
1238
1636
  test "|-start|" {
1239
- try log.start(p2.ident(6), .Bide);
1637
+ try log.start(.{ p2.ident(6), .Bide });
1240
1638
  try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1241
1639
  stream.reset();
1242
1640
 
1243
- try log.start(p1.ident(2), .ConfusionSilent);
1641
+ try log.start(.{ p1.ident(2), .ConfusionSilent });
1244
1642
  try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1245
1643
  stream.reset();
1246
1644
 
1247
- 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
+ });
1248
1651
  try expectLog1(
1249
1652
  &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1250
1653
  buf[0..5],
1251
1654
  );
1252
1655
  stream.reset();
1253
1656
 
1254
- 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
+ });
1255
1663
  try expectLog1(
1256
1664
  &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1257
1665
  buf[0..5],
1258
1666
  );
1259
1667
  stream.reset();
1260
1668
 
1261
- try log.startEffect(p1.ident(2), .Disable, M.Surf);
1262
- 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]);
1263
1671
  stream.reset();
1264
1672
 
1265
- try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1266
- 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]);
1267
1675
  stream.reset();
1268
1676
  }
1269
1677
 
1270
1678
  test "|-end|" {
1271
- try log.end(p2.ident(6), .Bide);
1679
+ try log.end(.{ p2.ident(6), .Bide });
1272
1680
  try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1273
1681
  stream.reset();
1274
1682
 
1275
- try log.end(p1.ident(2), .ConfusionSilent);
1683
+ try log.end(.{ p1.ident(2), .ConfusionSilent });
1276
1684
  try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1277
1685
  stream.reset();
1278
1686
  }
1279
1687
 
1280
1688
  test "|-ohko|" {
1281
- try log.ohko();
1689
+ try log.ohko(.{});
1282
1690
  try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1283
1691
  stream.reset();
1284
1692
  }
1693
+
1285
1694
  test "|-crit|" {
1286
- try log.crit(p2.ident(5));
1695
+ try log.crit(.{p2.ident(5)});
1287
1696
  try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1288
1697
  stream.reset();
1289
1698
  }
1699
+
1290
1700
  test "|-supereffective|" {
1291
- try log.supereffective(p1.ident(1));
1701
+ try log.supereffective(.{p1.ident(1)});
1292
1702
  try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1293
1703
  stream.reset();
1294
1704
  }
1705
+
1295
1706
  test "|-resisted|" {
1296
- try log.resisted(p2.ident(2));
1707
+ try log.resisted(.{p2.ident(2)});
1297
1708
  try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1298
1709
  stream.reset();
1299
1710
  }
1711
+
1300
1712
  test "|-immune|" {
1301
- try log.immune(p1.ident(3), .None);
1713
+ try log.immune(.{ p1.ident(3), .None });
1302
1714
  try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1303
1715
  stream.reset();
1304
1716
 
1305
- try log.immune(p2.ident(2), .OHKO);
1717
+ try log.immune(.{ p2.ident(2), .OHKO });
1306
1718
  try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1307
1719
  stream.reset();
1308
1720
  }
1721
+
1309
1722
  test "|-transform|" {
1310
- try log.transform(p2.ident(4), p1.ident(5));
1723
+ try log.transform(.{ p2.ident(4), p1.ident(5) });
1311
1724
  try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1312
1725
  stream.reset();
1313
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
+ }