@pkmn/engine 0.1.0-dev.fb5bf955 → 0.1.0-dev.fc150752

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 +2 -2
  3. package/build/pkg/addon.js +8 -7
  4. package/build/pkg/addon.js.map +1 -1
  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 +26 -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 +14 -26
  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 +315 -197
  29. package/src/lib/common/rational.zig +353 -0
  30. package/src/lib/common/rng.zig +11 -11
  31. package/src/lib/common/util.zig +12 -0
  32. package/src/lib/gen1/README.md +265 -155
  33. package/src/lib/gen1/calc.zig +426 -0
  34. package/src/lib/gen1/chance.zig +1036 -0
  35. package/src/lib/gen1/data/moves.zig +178 -180
  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 +73 -59
  39. package/src/lib/gen1/helpers.zig +592 -32
  40. package/src/lib/gen1/mechanics.zig +994 -641
  41. package/src/lib/gen2/README.md +87 -13
  42. package/src/lib/gen2/data/items.zig +3 -3
  43. package/src/lib/gen2/data/moves.zig +635 -563
  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 +104 -74
  47. package/src/lib/gen2/helpers.zig +16 -22
  48. package/src/lib/gen2/mechanics.zig +916 -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,
@@ -586,14 +656,18 @@ pub const ByteStream = struct {
586
656
  };
587
657
 
588
658
  pub const Kind = enum { Move, Species, Type, Status };
589
- pub const Formatter = fn (Kind, u8) []const u8;
590
659
 
591
- pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
660
+ pub fn format(
661
+ comptime gen: anytype,
662
+ a: []const u8,
663
+ b: ?[]const u8,
664
+ color: bool,
665
+ ) void {
592
666
  print("\n", .{});
593
667
 
594
668
  var i: usize = 0;
595
669
  while (i < a.len) {
596
- const arg = @intToEnum(ArgType, a[i]);
670
+ const arg: ArgType = @enumFromInt(a[i]);
597
671
  const name = switch (arg) {
598
672
  .None => if (color) "\x1b[2m-\x1b[0m" else "-",
599
673
  .LastStill => "|[still]",
@@ -639,19 +713,29 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
639
713
  .OHKO,
640
714
  => {},
641
715
  .Move => {
642
- const source = ID.from(@intCast(u4, a[i]));
716
+ const source = ID.from(@intCast(a[i]));
643
717
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
644
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
645
- const target = ID.from(@intCast(u4, a[i]));
718
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
719
+ const target = ID.from(@intCast(a[i]));
646
720
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
647
- const reason = @intToEnum(Move, a[i]);
721
+ const reason: Move = @enumFromInt(a[i]);
648
722
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
649
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
723
+ if (reason == .From) {
724
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
725
+ }
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
- printc(" {s}", .{formatter(.Species, a[i])}, a, b, &i, 1, color);
730
+ printc(" {s}", .{formatter(gen, .Species, a[i])}, a, b, &i, 1, color);
731
+ if (@hasDecl(gen, "Gender")) {
732
+ if (a[i] == N(gen.Gender.Unknown)) {
733
+ i += 1;
734
+ } else {
735
+ var gender = if (a[i] == N(gen.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 => {
@@ -667,15 +751,15 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
667
751
  printc("/{d}", .{hp}, a, b, &i, 2, color);
668
752
  },
669
753
  }
670
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
754
+ printc(" {s}", .{formatter(gen, .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
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
762
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
679
763
  }
680
764
  },
681
765
  .Faint,
@@ -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 => {
@@ -713,93 +799,96 @@ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, colo
713
799
  printc("/{d}", .{hp}, a, b, &i, 2, color);
714
800
  },
715
801
  }
716
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
802
+ printc(" {s}", .{formatter(gen, .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
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
737
- const reason = @intToEnum(Status, a[i]);
822
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
823
+ const reason: Status = @enumFromInt(a[i]);
738
824
  printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
739
- if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
825
+ if (reason == .From) {
826
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
827
+ }
740
828
  },
741
829
  .CureStatus => {
742
- const id = ID.from(@intCast(u4, a[i]));
830
+ const id = ID.from(@intCast(a[i]));
743
831
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
744
- printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
745
- printc(" {s}", .{@tagName(@intToEnum(CureStatus, a[i]))}, a, b, &i, 1, color);
832
+ printc(" {s}", .{formatter(gen, .Status, a[i])}, a, b, &i, 1, color);
833
+ const reason: CureStatus = @enumFromInt(a[i]);
834
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
746
835
  },
747
836
  .Boost => {
748
- const id = ID.from(@intCast(u4, a[i]));
837
+ const id = ID.from(@intCast(a[i]));
749
838
  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);
839
+ printc(" {s}", .{@tagName(@as(Boost, @enumFromInt(a[i])))}, a, b, &i, 1, color);
751
840
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
752
841
  },
753
842
  .Fail => {
754
- const id = ID.from(@intCast(u4, a[i]));
843
+ const id = ID.from(@intCast(a[i]));
755
844
  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);
845
+ printc(" {s}", .{@tagName(@as(Fail, @enumFromInt(a[i])))}, a, b, &i, 1, color);
757
846
  },
758
847
  .HitCount => {
759
- const id = ID.from(@intCast(u4, a[i]));
848
+ const id = ID.from(@intCast(a[i]));
760
849
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
761
850
  printc(" {d}", .{a[i]}, a, b, &i, 1, color);
762
851
  },
763
852
  .Prepare => {
764
- const id = ID.from(@intCast(u4, a[i]));
853
+ const id = ID.from(@intCast(a[i]));
765
854
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
766
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
855
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
767
856
  },
768
857
  .Activate => {
769
- const id = ID.from(@intCast(u4, a[i]));
858
+ const id = ID.from(@intCast(a[i]));
770
859
  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);
860
+ printc(" {s}", .{@tagName(@as(Activate, @enumFromInt(a[i])))}, a, b, &i, 1, color);
772
861
  },
773
862
  .Start => {
774
- const id = ID.from(@intCast(u4, a[i]));
863
+ const id = ID.from(@intCast(a[i]));
775
864
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
776
865
  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]);
866
+ printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
867
+ if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
868
+ const types = @as(gen1.Types, @bitCast(a[i]));
780
869
  const args = .{
781
- formatter(.Type, @enumToInt(types.type1)),
782
- formatter(.Type, @enumToInt(types.type2)),
870
+ formatter(gen, .Type, @intFromEnum(types.type1)),
871
+ formatter(gen, .Type, @intFromEnum(types.type2)),
783
872
  };
784
873
  printc(" {s}/{s}", args, a, b, &i, 1, color);
785
- } else if (reason >= @enumToInt(Start.Disable)) {
786
- printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
874
+ } else if (reason >= @intFromEnum(Start.Disable)) {
875
+ printc(" {s}", .{formatter(gen, .Move, a[i])}, a, b, &i, 1, color);
787
876
  }
788
877
  },
789
878
  .End => {
790
- const id = ID.from(@intCast(u4, a[i]));
879
+ const id = ID.from(@intCast(a[i]));
791
880
  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);
881
+ printc(" {s}", .{@tagName(@as(End, @enumFromInt(a[i])))}, a, b, &i, 1, color);
793
882
  },
794
883
  .Immune => {
795
- const id = ID.from(@intCast(u4, a[i]));
884
+ const id = ID.from(@intCast(a[i]));
796
885
  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);
886
+ printc(" {s}", .{@tagName(@as(Immune, @enumFromInt(a[i])))}, a, b, &i, 1, color);
798
887
  },
799
888
  .Transform => {
800
- const source = ID.from(@intCast(u4, a[i]));
889
+ const source = ID.from(@intCast(a[i]));
801
890
  printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
802
- const target = ID.from(@intCast(u4, a[i]));
891
+ const target = ID.from(@intCast(a[i]));
803
892
  printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
804
893
  },
805
894
  else => unreachable,
@@ -833,12 +922,13 @@ fn printc(
833
922
  }
834
923
 
835
924
  pub fn expectLog(
836
- comptime formatter: Formatter,
925
+ comptime gen: anytype,
837
926
  expected: []const u8,
838
927
  actual: []const u8,
839
928
  offset: usize,
840
929
  ) !void {
841
- if (!trace) return;
930
+ if (!enabled) return;
931
+
842
932
  const color = color: {
843
933
  if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
844
934
  break :color true;
@@ -851,8 +941,8 @@ pub fn expectLog(
851
941
 
852
942
  expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
853
943
  error.TestExpectedEqual => {
854
- format(formatter, expected, null, color);
855
- format(formatter, actual, expected, color);
944
+ format(gen, expected, null, color);
945
+ format(gen, actual, expected, color);
856
946
  return err;
857
947
  },
858
948
  else => return err,
@@ -881,7 +971,7 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
881
971
  const endian = builtin.target.cpu.arch.endian();
882
972
 
883
973
  fn N(e: anytype) u8 {
884
- return @enumToInt(e);
974
+ return @intFromEnum(e);
885
975
  }
886
976
 
887
977
  const p1 = Player.P1;
@@ -892,6 +982,11 @@ const gen1 = struct {
892
982
  pub const helpers = @import("../gen1/helpers.zig");
893
983
  };
894
984
 
985
+ const gen2 = struct {
986
+ pub usingnamespace @import("../gen2/data.zig");
987
+ pub const helpers = @import("../gen2/helpers.zig");
988
+ };
989
+
895
990
  var buf: [gen1.LOGS_SIZE]u8 = undefined;
896
991
  var stream = ByteStream{ .buffer = &buf };
897
992
  var log: FixedLog = .{ .writer = stream.writer() };
@@ -900,15 +995,19 @@ const M = gen1.Move;
900
995
  const S = gen1.Species;
901
996
 
902
997
  fn expectLog1(expected: []const u8, actual: []const u8) !void {
903
- return expectLog(gen1Formatter, expected, actual, 0);
998
+ return expectLog(gen1, expected, actual, 0);
904
999
  }
905
1000
 
906
- fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
1001
+ fn expectLog2(expected: []const u8, actual: []const u8) !void {
1002
+ return expectLog(gen2, expected, actual, 0);
1003
+ }
1004
+
1005
+ fn formatter(comptime gen: anytype, kind: Kind, byte: u8) []const u8 {
907
1006
  return switch (kind) {
908
- .Move => @tagName(@intToEnum(M, byte)),
909
- .Species => @tagName(@intToEnum(S, byte)),
910
- .Type => @tagName(@intToEnum(gen1.Type, byte)),
911
- .Status => gen1.Status.name(byte),
1007
+ .Move => @tagName(@as(gen.Move, @enumFromInt(byte))),
1008
+ .Species => @tagName(@as(gen.Species, @enumFromInt(byte))),
1009
+ .Type => @tagName(@as(gen.Type, @enumFromInt(byte))),
1010
+ .Status => gen.Status.name(byte),
912
1011
  };
913
1012
  }
914
1013
 
@@ -993,6 +1092,25 @@ test "|switch|" {
993
1092
  };
994
1093
  try expectLog1(expected, buf[0..9]);
995
1094
  stream.reset();
1095
+
1096
+ var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.Splash} });
1097
+ blissey.level = 91;
1098
+ blissey.hp = 200;
1099
+ blissey.stats.hp = 400;
1100
+ blissey.status = gen2.Status.init(.PAR);
1101
+ try log.switched(p2.ident(3), &blissey);
1102
+ expected = &(.{
1103
+ N(ArgType.Switch),
1104
+ 0b1011,
1105
+ N(gen2.Species.Blissey),
1106
+ N(gen2.Gender.Female),
1107
+ 91,
1108
+ } ++ switch (endian) {
1109
+ .Big => .{ 0, 200, 1, 144, par },
1110
+ .Little => .{ 200, 0, 144, 1, par },
1111
+ });
1112
+ try expectLog2(expected, buf[0..10]);
1113
+ stream.reset();
996
1114
  }
997
1115
 
998
1116
  test "|cant|" {