@pkmn/engine 0.1.0-dev.979a393a → 0.1.0-dev.a02611a4

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 (49) hide show
  1. package/README.md +71 -60
  2. package/build/pkg/addon.d.ts +7 -0
  3. package/build/pkg/addon.js +112 -0
  4. package/build/pkg/addon.js.map +1 -0
  5. package/build/pkg/data/layout.json +25 -3
  6. package/build/pkg/gen1.d.ts +5 -4
  7. package/build/pkg/gen1.js +47 -31
  8. package/build/pkg/gen1.js.map +1 -1
  9. package/build/pkg/index.d.ts +15 -12
  10. package/build/pkg/index.js.map +1 -1
  11. package/build/pkg/protocol.d.ts +4 -2
  12. package/build/pkg/protocol.js +4 -2
  13. package/build/pkg/protocol.js.map +1 -1
  14. package/build/test/display.js +31 -13
  15. package/build/test/display.js.map +1 -1
  16. package/build.zig +95 -76
  17. package/package.json +29 -16
  18. package/src/bin/install-pkmn-engine +11 -6
  19. package/src/bin/pkmn-debug +29 -6
  20. package/src/lib/binding/node.zig +46 -141
  21. package/src/lib/binding/wasm.zig +6 -4
  22. package/src/lib/common/battle.zig +17 -0
  23. package/src/lib/common/data.zig +11 -11
  24. package/src/lib/common/debug.zig +21 -11
  25. package/src/lib/common/js.zig +118 -0
  26. package/src/lib/common/optional.zig +60 -0
  27. package/src/lib/common/options.zig +12 -3
  28. package/src/lib/common/protocol.zig +308 -182
  29. package/src/lib/common/rational.zig +353 -0
  30. package/src/lib/common/rng.zig +10 -10
  31. package/src/lib/common/util.zig +12 -0
  32. package/src/lib/gen1/README.md +262 -154
  33. package/src/lib/gen1/calc.zig +426 -0
  34. package/src/lib/gen1/chance.zig +1044 -0
  35. package/src/lib/gen1/data/moves.zig +10 -10
  36. package/src/lib/gen1/data/species.zig +2 -2
  37. package/src/lib/gen1/data/types.zig +3 -3
  38. package/src/lib/gen1/data.zig +69 -43
  39. package/src/lib/gen1/helpers.zig +592 -32
  40. package/src/lib/gen1/mechanics.zig +983 -626
  41. package/src/lib/gen2/README.md +12 -13
  42. package/src/lib/gen2/data/items.zig +3 -3
  43. package/src/lib/gen2/data/moves.zig +16 -3
  44. package/src/lib/gen2/data/species.zig +1 -1
  45. package/src/lib/gen2/data/types.zig +3 -3
  46. package/src/lib/gen2/data.zig +59 -41
  47. package/src/lib/gen2/helpers.zig +15 -16
  48. package/src/lib/gen2/mechanics.zig +354 -11
  49. package/src/lib/pkmn.zig +68 -9
@@ -7,7 +7,7 @@ const options = @import("./options.zig");
7
7
  const assert = std.debug.assert;
8
8
  const print = std.debug.print;
9
9
 
10
- const trace = options.trace;
10
+ const enabled = options.log;
11
11
 
12
12
  const Player = data.Player;
13
13
  const ID = data.ID;
@@ -102,6 +102,9 @@ pub const ArgType = enum(u8) {
102
102
  // Gen 8
103
103
  CanDynamax,
104
104
  SwapSideConditions,
105
+
106
+ // Gen 9
107
+ Terastallize,
105
108
  };
106
109
 
107
110
  pub const Move = enum(u8) {
@@ -133,6 +136,8 @@ pub const Damage = enum(u8) {
133
136
  Confusion,
134
137
  LeechSeed,
135
138
  RecoilOf,
139
+
140
+ Spikes,
136
141
  };
137
142
 
138
143
  pub const Status = enum(u8) {
@@ -219,8 +224,12 @@ pub const Immune = enum(u8) {
219
224
  OHKO,
220
225
  };
221
226
 
227
+ /// Null object pattern implementation of `Log` backed by a `std.io.null_writer`.
228
+ /// Ignores anything sent to it, though protocol logging should additionally be turned off
229
+ /// entirely with `options.log`.
222
230
  pub const NULL = Log(@TypeOf(std.io.null_writer)){ .writer = std.io.null_writer };
223
231
 
232
+ /// Logs protocol information to its `Writer` during a battle update when `options.log` is enabled.
224
233
  pub fn Log(comptime Writer: type) type {
225
234
  return struct {
226
235
  const Self = @This();
@@ -230,91 +239,108 @@ pub fn Log(comptime Writer: type) type {
230
239
  pub const Error = Writer.Error;
231
240
 
232
241
  pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
233
- if (!trace) return;
242
+ if (!enabled) return;
243
+
234
244
  assert(m != .None);
235
245
  try self.writer.writeAll(&.{
236
- @enumToInt(ArgType.Move),
237
- @bitCast(u8, source),
238
- @enumToInt(m),
239
- @bitCast(u8, target),
246
+ @intFromEnum(ArgType.Move),
247
+ @as(u8, @bitCast(source)),
248
+ @intFromEnum(m),
249
+ @as(u8, @bitCast(target)),
240
250
  });
241
- const none = &[_]u8{@enumToInt(Move.None)};
251
+ const none = &[_]u8{@intFromEnum(Move.None)};
242
252
  try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
243
253
  .Null => none,
244
254
  .Optional => if (from) |f| blk: {
245
- assert(@enumToInt(f) != 0);
246
- break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(f) };
255
+ assert(@intFromEnum(f) != 0);
256
+ break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
247
257
  } else none,
248
258
  else => blk: {
249
- assert(@enumToInt(from) != 0);
250
- break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(from) };
259
+ assert(@intFromEnum(from) != 0);
260
+ break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(from) };
251
261
  },
252
262
  });
253
263
  }
254
264
 
255
265
  pub fn switched(self: Self, ident: ID, pokemon: anytype) Error!void {
256
- if (!trace) return;
257
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Switch), @bitCast(u8, ident) });
258
- try self.writer.writeAll(&.{ @enumToInt(pokemon.species), pokemon.level });
266
+ if (!enabled) return;
267
+
268
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Switch), @as(u8, @bitCast(ident)) });
269
+ if (@hasField(@TypeOf(pokemon.*), "dvs")) {
270
+ try self.writer.writeAll(&.{
271
+ @intFromEnum(pokemon.species),
272
+ @intFromEnum(pokemon.dvs.gender),
273
+ pokemon.level,
274
+ });
275
+ } else {
276
+ try self.writer.writeAll(&.{ @intFromEnum(pokemon.species), pokemon.level });
277
+ }
259
278
  try self.writer.writeIntNative(u16, pokemon.hp);
260
279
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
261
280
  try self.writer.writeAll(&.{pokemon.status});
262
281
  }
263
282
 
264
283
  pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
265
- if (!trace) return;
284
+ if (!enabled) return;
285
+
266
286
  assert(reason != .Disable);
267
287
  try self.writer.writeAll(&.{
268
- @enumToInt(ArgType.Cant),
269
- @bitCast(u8, ident),
270
- @enumToInt(reason),
288
+ @intFromEnum(ArgType.Cant),
289
+ @as(u8, @bitCast(ident)),
290
+ @intFromEnum(reason),
271
291
  });
272
292
  }
273
293
 
274
294
  pub fn disabled(self: Self, ident: ID, m: anytype) Error!void {
275
- if (!trace) return;
295
+ if (!enabled) return;
296
+
276
297
  try self.writer.writeAll(&.{
277
- @enumToInt(ArgType.Cant),
278
- @bitCast(u8, ident),
279
- @enumToInt(Cant.Disable),
280
- @enumToInt(m),
298
+ @intFromEnum(ArgType.Cant),
299
+ @as(u8, @bitCast(ident)),
300
+ @intFromEnum(Cant.Disable),
301
+ @intFromEnum(m),
281
302
  });
282
303
  }
283
304
 
284
305
  pub fn faint(self: Self, ident: ID, done: bool) Error!void {
285
- if (!trace) return;
286
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Faint), @bitCast(u8, ident) });
287
- if (done) try self.writer.writeByte(@enumToInt(ArgType.None));
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));
288
310
  }
289
311
 
290
312
  pub fn turn(self: Self, num: u16) Error!void {
291
- if (!trace) return;
292
- try self.writer.writeByte(@enumToInt(ArgType.Turn));
313
+ if (!enabled) return;
314
+
315
+ try self.writer.writeByte(@intFromEnum(ArgType.Turn));
293
316
  try self.writer.writeIntNative(u16, num);
294
- try self.writer.writeByte(@enumToInt(ArgType.None));
317
+ try self.writer.writeByte(@intFromEnum(ArgType.None));
295
318
  }
296
319
 
297
320
  pub fn win(self: Self, player: Player) Error!void {
298
- if (!trace) return;
321
+ if (!enabled) return;
322
+
299
323
  try self.writer.writeAll(&.{
300
- @enumToInt(ArgType.Win),
301
- @enumToInt(player),
302
- @enumToInt(ArgType.None),
324
+ @intFromEnum(ArgType.Win),
325
+ @intFromEnum(player),
326
+ @intFromEnum(ArgType.None),
303
327
  });
304
328
  }
305
329
 
306
330
  pub fn tie(self: Self) Error!void {
307
- if (!trace) return;
308
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Tie), @enumToInt(ArgType.None) });
331
+ if (!enabled) return;
332
+
333
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Tie), @intFromEnum(ArgType.None) });
309
334
  }
310
335
 
311
336
  pub fn damage(self: Self, ident: ID, pokemon: anytype, reason: Damage) Error!void {
312
- if (!trace) return;
313
- assert(@enumToInt(reason) <= @enumToInt(Damage.LeechSeed));
314
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
337
+ if (!enabled) return;
338
+
339
+ assert(@intFromEnum(reason) <= @intFromEnum(Damage.LeechSeed));
340
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
315
341
  try self.writer.writeIntNative(u16, pokemon.hp);
316
342
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
317
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
343
+ try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
318
344
  }
319
345
 
320
346
  pub fn damageOf(
@@ -324,227 +350,271 @@ pub fn Log(comptime Writer: type) type {
324
350
  reason: Damage,
325
351
  source: ID,
326
352
  ) Error!void {
327
- if (!trace) return;
328
- assert(@enumToInt(reason) == @enumToInt(Damage.RecoilOf));
329
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
353
+ if (!enabled) return;
354
+
355
+ assert(@intFromEnum(reason) == @intFromEnum(Damage.RecoilOf));
356
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Damage), @as(u8, @bitCast(ident)) });
330
357
  try self.writer.writeIntNative(u16, pokemon.hp);
331
358
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
332
359
  try self.writer.writeAll(&.{
333
360
  pokemon.status,
334
- @enumToInt(reason),
335
- @bitCast(u8, source),
361
+ @intFromEnum(reason),
362
+ @as(u8, @bitCast(source)),
336
363
  });
337
364
  }
338
365
 
339
366
  pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) Error!void {
340
- if (!trace) return;
367
+ if (!enabled) return;
368
+
341
369
  assert(reason != .Drain);
342
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, ident) });
370
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(ident)) });
343
371
  try self.writer.writeIntNative(u16, pokemon.hp);
344
372
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
345
- try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
373
+ try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
346
374
  }
347
375
 
348
376
  pub fn drain(self: Self, source: ID, pokemon: anytype, target: ID) Error!void {
349
- if (!trace) return;
350
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, source) });
377
+ if (!enabled) return;
378
+
379
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(source)) });
351
380
  try self.writer.writeIntNative(u16, pokemon.hp);
352
381
  try self.writer.writeIntNative(u16, pokemon.stats.hp);
353
382
  try self.writer.writeAll(&.{
354
383
  pokemon.status,
355
- @enumToInt(Heal.Drain),
356
- @bitCast(u8, target),
384
+ @intFromEnum(Heal.Drain),
385
+ @as(u8, @bitCast(target)),
357
386
  });
358
387
  }
359
388
 
360
389
  pub fn status(self: Self, ident: ID, value: u8, reason: Status) Error!void {
361
- if (!trace) return;
390
+ if (!enabled) return;
391
+
362
392
  assert(reason != .From);
363
393
  try self.writer.writeAll(&.{
364
- @enumToInt(ArgType.Status),
365
- @bitCast(u8, ident),
394
+ @intFromEnum(ArgType.Status),
395
+ @as(u8, @bitCast(ident)),
366
396
  value,
367
- @enumToInt(reason),
397
+ @intFromEnum(reason),
368
398
  });
369
399
  }
370
400
 
371
401
  pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
372
- if (!trace) return;
402
+ if (!enabled) return;
403
+
373
404
  try self.writer.writeAll(&.{
374
- @enumToInt(ArgType.Status),
375
- @bitCast(u8, ident),
405
+ @intFromEnum(ArgType.Status),
406
+ @as(u8, @bitCast(ident)),
376
407
  value,
377
- @enumToInt(Status.From),
378
- @enumToInt(m),
408
+ @intFromEnum(Status.From),
409
+ @intFromEnum(m),
379
410
  });
380
411
  }
381
412
 
382
413
  pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
383
- if (!trace) return;
414
+ if (!enabled) return;
415
+
384
416
  try self.writer.writeAll(&.{
385
- @enumToInt(ArgType.CureStatus),
386
- @bitCast(u8, ident),
417
+ @intFromEnum(ArgType.CureStatus),
418
+ @as(u8, @bitCast(ident)),
387
419
  value,
388
- @enumToInt(reason),
420
+ @intFromEnum(reason),
389
421
  });
390
422
  }
391
423
 
392
424
  pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
393
- if (!trace) return;
425
+ if (!enabled) return;
426
+
394
427
  assert(num != 0 and (reason != .Rage or num > 0));
395
428
  try self.writer.writeAll(&.{
396
- @enumToInt(ArgType.Boost),
397
- @bitCast(u8, ident),
398
- @enumToInt(reason),
399
- @intCast(u8, num + 6),
429
+ @intFromEnum(ArgType.Boost),
430
+ @as(u8, @bitCast(ident)),
431
+ @intFromEnum(reason),
432
+ @as(u8, @intCast(num + 6)),
400
433
  });
401
434
  }
402
435
 
403
436
  pub fn clearallboost(self: Self) Error!void {
404
- if (!trace) return;
405
- try self.writer.writeAll(&.{@enumToInt(ArgType.ClearAllBoost)});
437
+ if (!enabled) return;
438
+
439
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.ClearAllBoost)});
406
440
  }
407
441
 
408
442
  pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
409
- if (!trace) return;
443
+ if (!enabled) return;
444
+
410
445
  try self.writer.writeAll(&.{
411
- @enumToInt(ArgType.Fail),
412
- @bitCast(u8, ident),
413
- @enumToInt(reason),
446
+ @intFromEnum(ArgType.Fail),
447
+ @as(u8, @bitCast(ident)),
448
+ @intFromEnum(reason),
414
449
  });
415
450
  }
416
451
 
417
452
  pub fn miss(self: Self, source: ID) Error!void {
418
- if (!trace) return;
419
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Miss), @bitCast(u8, source) });
453
+ if (!enabled) return;
454
+
455
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Miss), @as(u8, @bitCast(source)) });
420
456
  }
421
457
 
422
458
  pub fn hitcount(self: Self, ident: ID, num: u8) Error!void {
423
- if (!trace) return;
424
- try self.writer.writeAll(&.{ @enumToInt(ArgType.HitCount), @bitCast(u8, ident), num });
459
+ if (!enabled) return;
460
+
461
+ try self.writer.writeAll(&.{
462
+ @intFromEnum(ArgType.HitCount),
463
+ @as(u8, @bitCast(ident)),
464
+ num,
465
+ });
425
466
  }
426
467
 
427
468
  pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
428
- if (!trace) return;
469
+ if (!enabled) return;
470
+
429
471
  try self.writer.writeAll(&.{
430
- @enumToInt(ArgType.Prepare),
431
- @bitCast(u8, source),
432
- @enumToInt(m),
472
+ @intFromEnum(ArgType.Prepare),
473
+ @as(u8, @bitCast(source)),
474
+ @intFromEnum(m),
433
475
  });
434
476
  }
435
477
 
436
478
  pub fn mustrecharge(self: Self, ident: ID) Error!void {
437
- if (!trace) return;
438
- try self.writer.writeAll(&.{ @enumToInt(ArgType.MustRecharge), @bitCast(u8, ident) });
479
+ if (!enabled) return;
480
+
481
+ try self.writer.writeAll(&.{
482
+ @intFromEnum(ArgType.MustRecharge),
483
+ @as(u8, @bitCast(ident)),
484
+ });
439
485
  }
440
486
 
441
487
  pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
442
- if (!trace) return;
488
+ if (!enabled) return;
489
+
443
490
  try self.writer.writeAll(&.{
444
- @enumToInt(ArgType.Activate),
445
- @bitCast(u8, ident),
446
- @enumToInt(reason),
491
+ @intFromEnum(ArgType.Activate),
492
+ @as(u8, @bitCast(ident)),
493
+ @intFromEnum(reason),
447
494
  });
448
495
  }
449
496
 
450
497
  pub fn fieldactivate(self: Self) Error!void {
451
- if (!trace) return;
452
- try self.writer.writeAll(&.{@enumToInt(ArgType.FieldActivate)});
498
+ if (!enabled) return;
499
+
500
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.FieldActivate)});
453
501
  }
454
502
 
455
503
  pub fn start(self: Self, ident: ID, reason: Start) Error!void {
456
- if (!trace) return;
457
- assert(@enumToInt(reason) < @enumToInt(Start.TypeChange));
504
+ if (!enabled) return;
505
+
506
+ assert(@intFromEnum(reason) < @intFromEnum(Start.TypeChange));
458
507
  try self.writer.writeAll(&.{
459
- @enumToInt(ArgType.Start),
460
- @bitCast(u8, ident),
461
- @enumToInt(reason),
508
+ @intFromEnum(ArgType.Start),
509
+ @as(u8, @bitCast(ident)),
510
+ @intFromEnum(reason),
462
511
  });
463
512
  }
464
513
 
465
514
  pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
466
- if (!trace) return;
515
+ if (!enabled) return;
516
+
467
517
  try self.writer.writeAll(&.{
468
- @enumToInt(ArgType.Start),
469
- @bitCast(u8, source),
470
- @enumToInt(Start.TypeChange),
471
- @bitCast(u8, types),
472
- @bitCast(u8, target),
518
+ @intFromEnum(ArgType.Start),
519
+ @as(u8, @bitCast(source)),
520
+ @intFromEnum(Start.TypeChange),
521
+ @as(u8, @bitCast(types)),
522
+ @as(u8, @bitCast(target)),
473
523
  });
474
524
  }
475
525
 
476
526
  pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) Error!void {
477
- if (!trace) return;
478
- assert(@enumToInt(reason) > @enumToInt(Start.TypeChange));
527
+ if (!enabled) return;
528
+
529
+ assert(@intFromEnum(reason) > @intFromEnum(Start.TypeChange));
479
530
  try self.writer.writeAll(&.{
480
- @enumToInt(ArgType.Start),
481
- @bitCast(u8, ident),
482
- @enumToInt(reason),
483
- @enumToInt(m),
531
+ @intFromEnum(ArgType.Start),
532
+ @as(u8, @bitCast(ident)),
533
+ @intFromEnum(reason),
534
+ @intFromEnum(m),
484
535
  });
485
536
  }
486
537
 
487
538
  pub fn end(self: Self, ident: ID, reason: End) Error!void {
488
- if (!trace) return;
539
+ if (!enabled) return;
540
+
489
541
  try self.writer.writeAll(&.{
490
- @enumToInt(ArgType.End),
491
- @bitCast(u8, ident),
492
- @enumToInt(reason),
542
+ @intFromEnum(ArgType.End),
543
+ @as(u8, @bitCast(ident)),
544
+ @intFromEnum(reason),
493
545
  });
494
546
  }
495
547
 
496
548
  pub fn ohko(self: Self) Error!void {
497
- if (!trace) return;
498
- try self.writer.writeAll(&.{@enumToInt(ArgType.OHKO)});
549
+ if (!enabled) return;
550
+
551
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.OHKO)});
499
552
  }
500
553
 
501
554
  pub fn crit(self: Self, ident: ID) Error!void {
502
- if (!trace) return;
503
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Crit), @bitCast(u8, ident) });
555
+ if (!enabled) return;
556
+
557
+ try self.writer.writeAll(&.{ @intFromEnum(ArgType.Crit), @as(u8, @bitCast(ident)) });
504
558
  }
505
559
 
506
560
  pub fn supereffective(self: Self, ident: ID) Error!void {
507
- if (!trace) return;
508
- try self.writer.writeAll(&.{ @enumToInt(ArgType.SuperEffective), @bitCast(u8, ident) });
561
+ if (!enabled) return;
562
+
563
+ try self.writer.writeAll(&.{
564
+ @intFromEnum(ArgType.SuperEffective),
565
+ @as(u8, @bitCast(ident)),
566
+ });
509
567
  }
510
568
 
511
569
  pub fn resisted(self: Self, ident: ID) Error!void {
512
- if (!trace) return;
513
- try self.writer.writeAll(&.{ @enumToInt(ArgType.Resisted), @bitCast(u8, ident) });
570
+ if (!enabled) return;
571
+
572
+ try self.writer.writeAll(&.{
573
+ @intFromEnum(ArgType.Resisted),
574
+ @as(u8, @bitCast(ident)),
575
+ });
514
576
  }
515
577
 
516
578
  pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
517
- if (!trace) return;
579
+ if (!enabled) return;
580
+
518
581
  try self.writer.writeAll(&.{
519
- @enumToInt(ArgType.Immune),
520
- @bitCast(u8, ident),
521
- @enumToInt(reason),
582
+ @intFromEnum(ArgType.Immune),
583
+ @as(u8, @bitCast(ident)),
584
+ @intFromEnum(reason),
522
585
  });
523
586
  }
524
587
 
525
588
  pub fn transform(self: Self, source: ID, target: ID) Error!void {
526
- if (!trace) return;
589
+ if (!enabled) return;
590
+
527
591
  try self.writer.writeAll(&.{
528
- @enumToInt(ArgType.Transform),
529
- @bitCast(u8, source),
530
- @bitCast(u8, target),
592
+ @intFromEnum(ArgType.Transform),
593
+ @as(u8, @bitCast(source)),
594
+ @as(u8, @bitCast(target)),
531
595
  });
532
596
  }
533
597
 
534
598
  pub fn laststill(self: Self) Error!void {
535
- if (!trace) return;
536
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastStill)});
599
+ if (!enabled) return;
600
+
601
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
537
602
  }
538
603
 
539
604
  pub fn lastmiss(self: Self) Error!void {
540
- if (!trace) return;
541
- try self.writer.writeAll(&.{@enumToInt(ArgType.LastMiss)});
605
+ if (!enabled) return;
606
+
607
+ try self.writer.writeAll(&.{@intFromEnum(ArgType.LastMiss)});
542
608
  }
543
609
  };
544
610
  }
545
611
 
612
+ /// `Log` type backed by the optimized `ByteStream.Writer`.
546
613
  pub const FixedLog = Log(ByteStream.Writer);
547
614
 
615
+ /// Stripped down version of `std.io.FixedBufferStream` optimized for efficiently writing the
616
+ /// individual protocol bytes. Note that the `ByteStream.Writer` is **not** a `std.io.Writer` and
617
+ /// should not be used for general purpose writing.
548
618
  pub const ByteStream = struct {
549
619
  buffer: []u8,
550
620
  pos: usize = 0,
@@ -588,12 +658,18 @@ pub const ByteStream = struct {
588
658
  pub const Kind = enum { Move, Species, Type, Status };
589
659
  pub const Formatter = fn (Kind, u8) []const u8;
590
660
 
591
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
661
+ pub fn format(
662
+ comptime gen: comptime_int,
663
+ comptime formatter: Formatter,
664
+ a: []const u8,
665
+ b: ?[]const u8,
666
+ color: bool,
667
+ ) void {
592
668
  print("\n", .{});
593
669
 
594
670
  var i: usize = 0;
595
671
  while (i < a.len) {
596
- const arg = @intToEnum(ArgType, a[i]);
672
+ const arg: ArgType = @enumFromInt(a[i]);
597
673
  const name = switch (arg) {
598
674
  .None => if (color) "\x1b[2m-\x1b[0m" else "-",
599
675
  .LastStill => "|[still]",
@@ -639,19 +715,27 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
639
715
  .OHKO,
640
716
  => {},
641
717
  .Move => {
642
- const source = ID.from(@intCast(u4, a[i]));
718
+ const source = ID.from(@intCast(a[i]));
643
719
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
644
720
  printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
645
- const target = ID.from(@intCast(u4, a[i]));
721
+ const target = ID.from(@intCast(a[i]));
646
722
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
647
- const reason = @intToEnum(Move, a[i]);
723
+ const reason: Move = @enumFromInt(a[i]);
648
724
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
649
725
  if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
650
726
  },
651
727
  .Switch => {
652
- const id = ID.from(@intCast(u4, a[i]));
728
+ const id = ID.from(@intCast(a[i]));
653
729
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
654
730
  printc(" {s}", .{formatter(.Species, a[i])}, a, b, &i, 1, color);
731
+ if (gen > 1) {
732
+ if (a[i] == N(gen2.Gender.Unknown)) {
733
+ i += 1;
734
+ } else {
735
+ var gender = if (a[i] == N(gen2.Gender.Male)) "M" else "F";
736
+ printc(", {s}", .{gender}, a, b, &i, 1, color);
737
+ }
738
+ }
655
739
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
656
740
  switch (endian) {
657
741
  .Big => {
@@ -670,9 +754,9 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
670
754
  printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
671
755
  },
672
756
  .Cant => {
673
- const id = ID.from(@intCast(u4, a[i]));
757
+ const id = ID.from(@intCast(a[i]));
674
758
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
675
- const reason = @intToEnum(Cant, a[i]);
759
+ const reason: Cant = @enumFromInt(a[i]);
676
760
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
677
761
  if (reason == .Disable) {
678
762
  printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
@@ -685,7 +769,7 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
685
769
  .SuperEffective,
686
770
  .Resisted,
687
771
  => {
688
- const id = ID.from(@intCast(u4, a[i]));
772
+ const id = ID.from(@intCast(a[i]));
689
773
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
690
774
  },
691
775
  .Turn => {
@@ -695,9 +779,11 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
695
779
  };
696
780
  printc(" {d}", .{turn}, a, b, &i, 2, color);
697
781
  },
698
- .Win => printc(" {s}", .{@tagName(@intToEnum(Player, a[i]))}, a, b, &i, 1, color),
782
+ .Win => {
783
+ printc(" {s}", .{@tagName(@as(Player, @enumFromInt(a[i])))}, a, b, &i, 1, color);
784
+ },
699
785
  .Damage, .Heal => {
700
- var id = ID.from(@intCast(u4, a[i]));
786
+ var id = ID.from(@intCast(a[i]));
701
787
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
702
788
  switch (endian) {
703
789
  .Big => {
@@ -715,91 +801,92 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
715
801
  }
716
802
  printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
717
803
  if (arg == .Damage) {
718
- const reason = a[i];
719
- printc(" {s}", .{@tagName(@intToEnum(Damage, reason))}, a, b, &i, 1, color);
720
- if (reason == @enumToInt(Damage.RecoilOf)) {
721
- id = ID.from(@intCast(u4, a[i]));
804
+ const reason: Damage = @enumFromInt(a[i]);
805
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
806
+ if (reason == .RecoilOf) {
807
+ id = ID.from(@intCast(a[i]));
722
808
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
723
809
  }
724
810
  } else {
725
- const reason = @intToEnum(Heal, a[i]);
811
+ const reason: Heal = @enumFromInt(a[i]);
726
812
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
727
813
  if (reason == .Drain) {
728
- id = ID.from(@intCast(u4, a[i]));
814
+ id = ID.from(@intCast(a[i]));
729
815
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
730
816
  }
731
817
  }
732
818
  },
733
819
  .Status => {
734
- const id = ID.from(@intCast(u4, a[i]));
820
+ const id = ID.from(@intCast(a[i]));
735
821
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
736
822
  printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
737
- const reason = @intToEnum(Status, a[i]);
823
+ const reason: Status = @enumFromInt(a[i]);
738
824
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
739
825
  if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
740
826
  },
741
827
  .CureStatus => {
742
- const id = ID.from(@intCast(u4, a[i]));
828
+ const id = ID.from(@intCast(a[i]));
743
829
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
744
830
  printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
745
- printc(" {s}", .{@tagName(@intToEnum(CureStatus, a[i]))}, a, b, &i, 1, color);
831
+ const reason: CureStatus = @enumFromInt(a[i]);
832
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
746
833
  },
747
834
  .Boost => {
748
- const id = ID.from(@intCast(u4, a[i]));
835
+ const id = ID.from(@intCast(a[i]));
749
836
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
750
- printc(" {s}", .{@tagName(@intToEnum(Boost, a[i]))}, a, b, &i, 1, color);
837
+ printc(" {s}", .{@tagName(@as(Boost, @enumFromInt(a[i])))}, a, b, &i, 1, color);
751
838
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
752
839
  },
753
840
  .Fail => {
754
- const id = ID.from(@intCast(u4, a[i]));
841
+ const id = ID.from(@intCast(a[i]));
755
842
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
756
- printc(" {s}", .{@tagName(@intToEnum(Fail, a[i]))}, a, b, &i, 1, color);
843
+ printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
757
844
  },
758
845
  .HitCount => {
759
- const id = ID.from(@intCast(u4, a[i]));
846
+ const id = ID.from(@intCast(a[i]));
760
847
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
761
848
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
762
849
  },
763
850
  .Prepare => {
764
- const id = ID.from(@intCast(u4, a[i]));
851
+ const id = ID.from(@intCast(a[i]));
765
852
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
766
853
  printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
767
854
  },
768
855
  .Activate => {
769
- const id = ID.from(@intCast(u4, a[i]));
856
+ const id = ID.from(@intCast(a[i]));
770
857
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
771
- printc(" {s}", .{@tagName(@intToEnum(Activate, a[i]))}, a, b, &i, 1, color);
858
+ printc(" {s}", .{@tagName(@as(Activate, @enumFromInt(a[i])))}, a, b, &i, 1, color);
772
859
  },
773
860
  .Start => {
774
- const id = ID.from(@intCast(u4, a[i]));
861
+ const id = ID.from(@intCast(a[i]));
775
862
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
776
863
  const reason = a[i];
777
- printc(" {s}", .{@tagName(@intToEnum(Start, reason))}, a, b, &i, 1, color);
778
- if (@intToEnum(Start, reason) == .TypeChange) {
779
- const types = @bitCast(gen1.Types, a[i]);
864
+ printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
865
+ if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
866
+ const types = @as(gen1.Types, @bitCast(a[i]));
780
867
  const args = .{
781
- formatter(.Type, @enumToInt(types.type1)),
782
- formatter(.Type, @enumToInt(types.type2)),
868
+ formatter(.Type, @intFromEnum(types.type1)),
869
+ formatter(.Type, @intFromEnum(types.type2)),
783
870
  };
784
871
  printc(" {s}/{s}", args, a, b, &i, 1, color);
785
- } else if (reason >= @enumToInt(Start.Disable)) {
872
+ } else if (reason >= @intFromEnum(Start.Disable)) {
786
873
  printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
787
874
  }
788
875
  },
789
876
  .End => {
790
- const id = ID.from(@intCast(u4, a[i]));
877
+ const id = ID.from(@intCast(a[i]));
791
878
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
792
- printc(" {s}", .{@tagName(@intToEnum(End, a[i]))}, a, b, &i, 1, color);
879
+ printc(" {s}", .{@tagName(@as(End, @enumFromInt(a[i])))}, a, b, &i, 1, color);
793
880
  },
794
881
  .Immune => {
795
- const id = ID.from(@intCast(u4, a[i]));
882
+ const id = ID.from(@intCast(a[i]));
796
883
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
797
- printc(" {s}", .{@tagName(@intToEnum(Immune, a[i]))}, a, b, &i, 1, color);
884
+ printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
798
885
  },
799
886
  .Transform => {
800
- const source = ID.from(@intCast(u4, a[i]));
887
+ const source = ID.from(@intCast(a[i]));
801
888
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
802
- const target = ID.from(@intCast(u4, a[i]));
889
+ const target = ID.from(@intCast(a[i]));
803
890
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
804
891
  },
805
892
  else => unreachable,
@@ -833,12 +920,14 @@ fn printc(
833
920
  }
834
921
 
835
922
  pub fn expectLog(
923
+ comptime gen: comptime_int,
836
924
  comptime formatter: Formatter,
837
925
  expected: []const u8,
838
926
  actual: []const u8,
839
927
  offset: usize,
840
928
  ) !void {
841
- if (!trace) return;
929
+ if (!enabled) return;
930
+
842
931
  const color = color: {
843
932
  if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
844
933
  break :color true;
@@ -851,8 +940,8 @@ pub fn expectLog(
851
940
 
852
941
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
853
942
  error.TestExpectedEqual => {
854
- format(formatter, expected, null, color);
855
- format(formatter, actual, expected, color);
943
+ format(gen, formatter, expected, null, color);
944
+ format(gen, formatter, actual, expected, color);
856
945
  return err;
857
946
  },
858
947
  else => return err,
@@ -881,7 +970,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
881
970
  const endian = builtin.target.cpu.arch.endian();
882
971
 
883
972
  fn N(e: anytype) u8 {
884
- return @enumToInt(e);
973
+ return @intFromEnum(e);
885
974
  }
886
975
 
887
976
  const p1 = Player.P1;
@@ -892,6 +981,11 @@ const gen1 = struct {
892
981
  pub const helpers = @import("../gen1/helpers.zig");
893
982
  };
894
983
 
984
+ const gen2 = struct {
985
+ pub usingnamespace @import("../gen2/data.zig");
986
+ pub const helpers = @import("../gen2/helpers.zig");
987
+ };
988
+
895
989
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
896
990
  var stream = ByteStream{ .buffer = &buf };
897
991
  var log: FixedLog = .{ .writer = stream.writer() };
@@ -900,18 +994,31 @@ const M = gen1.Move;
900
994
  const S = gen1.Species;
901
995
 
902
996
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
903
- return expectLog(gen1Formatter, expected, actual, 0);
997
+ return expectLog(1, gen1Formatter, expected, actual, 0);
998
+ }
999
+
1000
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1001
+ return expectLog(2, gen2Formatter, expected, actual, 0);
904
1002
  }
905
1003
 
906
1004
  fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
907
1005
  return switch (kind) {
908
- .Move => @tagName(@intToEnum(M, byte)),
909
- .Species => @tagName(@intToEnum(S, byte)),
910
- .Type => @tagName(@intToEnum(gen1.Type, byte)),
1006
+ .Move => @tagName(@as(gen1.Move, @enumFromInt(byte))),
1007
+ .Species => @tagName(@as(gen1.Species, @enumFromInt(byte))),
1008
+ .Type => @tagName(@as(gen1.Type, @enumFromInt(byte))),
911
1009
  .Status => gen1.Status.name(byte),
912
1010
  };
913
1011
  }
914
1012
 
1013
+ fn gen2Formatter(kind: Kind, byte: u8) []const u8 {
1014
+ return switch (kind) {
1015
+ .Move => @tagName(@as(gen2.Move, @enumFromInt(byte))),
1016
+ .Species => @tagName(@as(gen2.Species, @enumFromInt(byte))),
1017
+ .Type => @tagName(@as(gen2.Type, @enumFromInt(byte))),
1018
+ .Status => gen2.Status.name(byte),
1019
+ };
1020
+ }
1021
+
915
1022
  test "|move|" {
916
1023
  try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
917
1024
  try expectLog1(
@@ -993,6 +1100,25 @@ test "|switch|" {
993
1100
  };
994
1101
  try expectLog1(expected, buf[0..9]);
995
1102
  stream.reset();
1103
+
1104
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1105
+ blissey.level = 91;
1106
+ blissey.hp = 200;
1107
+ blissey.stats.hp = 400;
1108
+ blissey.status = gen2.Status.init(.PAR);
1109
+ try log.switched(p2.ident(3), &blissey);
1110
+ expected = &(.{
1111
+ N(ArgType.Switch),
1112
+ 0b1011,
1113
+ N(gen2.Species.Blissey),
1114
+ N(gen2.Gender.Female),
1115
+ 91,
1116
+ } ++ switch (endian) {
1117
+ .Big => .{ 0, 200, 1, 144, par },
1118
+ .Little => .{ 200, 0, 144, 1, par },
1119
+ });
1120
+ try expectLog2(expected, buf[0..10]);
1121
+ stream.reset();
996
1122
  }
997
1123
 
998
1124
  test "|cant|" {