@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.d1b39ff0

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