@pkmn/engine 0.1.0-dev.d3e796d5 → 0.1.0-dev.d4bd80b1

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