@pkmn/engine 0.1.0-dev.90d6c14c → 0.1.0-dev.9257876b

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