@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.dd5b9081
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.
- package/README.md +55 -44
- package/build/pkg/addon.js +3 -2
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +24 -2
- package/build/pkg/gen1.js +13 -16
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build.zig +86 -62
- package/package.json +25 -16
- package/src/bin/install-pkmn-engine +10 -5
- package/src/lib/binding/node.zig +45 -140
- package/src/lib/binding/wasm.zig +4 -2
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +14 -26
- package/src/lib/common/debug.zig +20 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +9 -0
- package/src/lib/common/protocol.zig +277 -159
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +259 -177
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1036 -0
- package/src/lib/gen1/data/moves.zig +178 -180
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +66 -54
- package/src/lib/gen1/helpers.zig +589 -18
- package/src/lib/gen1/mechanics.zig +791 -508
- package/src/lib/gen2/README.md +87 -13
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +624 -563
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +3 -3
- package/src/lib/gen2/data.zig +100 -70
- package/src/lib/gen2/helpers.zig +16 -22
- package/src/lib/gen2/mechanics.zig +714 -10
- package/src/lib/pkmn.zig +61 -2
|
@@ -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();
|
|
@@ -231,31 +240,41 @@ pub fn Log(comptime Writer: type) type {
|
|
|
231
240
|
|
|
232
241
|
pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
|
|
233
242
|
if (!enabled) return;
|
|
243
|
+
|
|
234
244
|
assert(m != .None);
|
|
235
245
|
try self.writer.writeAll(&.{
|
|
236
|
-
@
|
|
237
|
-
@
|
|
238
|
-
@
|
|
239
|
-
@
|
|
246
|
+
@intFromEnum(ArgType.Move),
|
|
247
|
+
@as(u8, @bitCast(source)),
|
|
248
|
+
@intFromEnum(m),
|
|
249
|
+
@as(u8, @bitCast(target)),
|
|
240
250
|
});
|
|
241
|
-
const none = &[_]u8{@
|
|
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(@
|
|
246
|
-
break :blk &[_]u8{ @
|
|
255
|
+
assert(@intFromEnum(f) != 0);
|
|
256
|
+
break :blk &[_]u8{ @intFromEnum(Move.From), @intFromEnum(f) };
|
|
247
257
|
} else none,
|
|
248
258
|
else => blk: {
|
|
249
|
-
assert(@
|
|
250
|
-
break :blk &[_]u8{ @
|
|
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
266
|
if (!enabled) return;
|
|
257
|
-
|
|
258
|
-
try self.writer.writeAll(&.{ @
|
|
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});
|
|
@@ -263,58 +282,65 @@ pub fn Log(comptime Writer: type) type {
|
|
|
263
282
|
|
|
264
283
|
pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
|
|
265
284
|
if (!enabled) return;
|
|
285
|
+
|
|
266
286
|
assert(reason != .Disable);
|
|
267
287
|
try self.writer.writeAll(&.{
|
|
268
|
-
@
|
|
269
|
-
@
|
|
270
|
-
@
|
|
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
295
|
if (!enabled) return;
|
|
296
|
+
|
|
276
297
|
try self.writer.writeAll(&.{
|
|
277
|
-
@
|
|
278
|
-
@
|
|
279
|
-
@
|
|
280
|
-
@
|
|
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
306
|
if (!enabled) return;
|
|
286
|
-
|
|
287
|
-
|
|
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
313
|
if (!enabled) return;
|
|
292
|
-
|
|
314
|
+
|
|
315
|
+
try self.writer.writeByte(@intFromEnum(ArgType.Turn));
|
|
293
316
|
try self.writer.writeIntNative(u16, num);
|
|
294
|
-
try self.writer.writeByte(@
|
|
317
|
+
try self.writer.writeByte(@intFromEnum(ArgType.None));
|
|
295
318
|
}
|
|
296
319
|
|
|
297
320
|
pub fn win(self: Self, player: Player) Error!void {
|
|
298
321
|
if (!enabled) return;
|
|
322
|
+
|
|
299
323
|
try self.writer.writeAll(&.{
|
|
300
|
-
@
|
|
301
|
-
@
|
|
302
|
-
@
|
|
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
331
|
if (!enabled) return;
|
|
308
|
-
|
|
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
337
|
if (!enabled) return;
|
|
313
|
-
|
|
314
|
-
|
|
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, @
|
|
343
|
+
try self.writer.writeAll(&.{ pokemon.status, @intFromEnum(reason) });
|
|
318
344
|
}
|
|
319
345
|
|
|
320
346
|
pub fn damageOf(
|
|
@@ -325,226 +351,270 @@ pub fn Log(comptime Writer: type) type {
|
|
|
325
351
|
source: ID,
|
|
326
352
|
) Error!void {
|
|
327
353
|
if (!enabled) return;
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
@
|
|
335
|
-
@
|
|
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
367
|
if (!enabled) return;
|
|
368
|
+
|
|
341
369
|
assert(reason != .Drain);
|
|
342
|
-
try self.writer.writeAll(&.{ @
|
|
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, @
|
|
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
377
|
if (!enabled) return;
|
|
350
|
-
|
|
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
|
-
@
|
|
356
|
-
@
|
|
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
390
|
if (!enabled) return;
|
|
391
|
+
|
|
362
392
|
assert(reason != .From);
|
|
363
393
|
try self.writer.writeAll(&.{
|
|
364
|
-
@
|
|
365
|
-
@
|
|
394
|
+
@intFromEnum(ArgType.Status),
|
|
395
|
+
@as(u8, @bitCast(ident)),
|
|
366
396
|
value,
|
|
367
|
-
@
|
|
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
402
|
if (!enabled) return;
|
|
403
|
+
|
|
373
404
|
try self.writer.writeAll(&.{
|
|
374
|
-
@
|
|
375
|
-
@
|
|
405
|
+
@intFromEnum(ArgType.Status),
|
|
406
|
+
@as(u8, @bitCast(ident)),
|
|
376
407
|
value,
|
|
377
|
-
@
|
|
378
|
-
@
|
|
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
414
|
if (!enabled) return;
|
|
415
|
+
|
|
384
416
|
try self.writer.writeAll(&.{
|
|
385
|
-
@
|
|
386
|
-
@
|
|
417
|
+
@intFromEnum(ArgType.CureStatus),
|
|
418
|
+
@as(u8, @bitCast(ident)),
|
|
387
419
|
value,
|
|
388
|
-
@
|
|
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
425
|
if (!enabled) return;
|
|
426
|
+
|
|
394
427
|
assert(num != 0 and (reason != .Rage or num > 0));
|
|
395
428
|
try self.writer.writeAll(&.{
|
|
396
|
-
@
|
|
397
|
-
@
|
|
398
|
-
@
|
|
399
|
-
@
|
|
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
437
|
if (!enabled) return;
|
|
405
|
-
|
|
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
443
|
if (!enabled) return;
|
|
444
|
+
|
|
410
445
|
try self.writer.writeAll(&.{
|
|
411
|
-
@
|
|
412
|
-
@
|
|
413
|
-
@
|
|
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
453
|
if (!enabled) return;
|
|
419
|
-
|
|
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
459
|
if (!enabled) return;
|
|
424
|
-
|
|
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
469
|
if (!enabled) return;
|
|
470
|
+
|
|
429
471
|
try self.writer.writeAll(&.{
|
|
430
|
-
@
|
|
431
|
-
@
|
|
432
|
-
@
|
|
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
479
|
if (!enabled) return;
|
|
438
|
-
|
|
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
488
|
if (!enabled) return;
|
|
489
|
+
|
|
443
490
|
try self.writer.writeAll(&.{
|
|
444
|
-
@
|
|
445
|
-
@
|
|
446
|
-
@
|
|
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
498
|
if (!enabled) return;
|
|
452
|
-
|
|
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
504
|
if (!enabled) return;
|
|
457
|
-
|
|
505
|
+
|
|
506
|
+
assert(@intFromEnum(reason) < @intFromEnum(Start.TypeChange));
|
|
458
507
|
try self.writer.writeAll(&.{
|
|
459
|
-
@
|
|
460
|
-
@
|
|
461
|
-
@
|
|
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
515
|
if (!enabled) return;
|
|
516
|
+
|
|
467
517
|
try self.writer.writeAll(&.{
|
|
468
|
-
@
|
|
469
|
-
@
|
|
470
|
-
@
|
|
471
|
-
@
|
|
472
|
-
@
|
|
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
527
|
if (!enabled) return;
|
|
478
|
-
|
|
528
|
+
|
|
529
|
+
assert(@intFromEnum(reason) > @intFromEnum(Start.TypeChange));
|
|
479
530
|
try self.writer.writeAll(&.{
|
|
480
|
-
@
|
|
481
|
-
@
|
|
482
|
-
@
|
|
483
|
-
@
|
|
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
539
|
if (!enabled) return;
|
|
540
|
+
|
|
489
541
|
try self.writer.writeAll(&.{
|
|
490
|
-
@
|
|
491
|
-
@
|
|
492
|
-
@
|
|
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
549
|
if (!enabled) return;
|
|
498
|
-
|
|
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
555
|
if (!enabled) return;
|
|
503
|
-
|
|
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
561
|
if (!enabled) return;
|
|
508
|
-
|
|
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
570
|
if (!enabled) return;
|
|
513
|
-
|
|
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
579
|
if (!enabled) return;
|
|
580
|
+
|
|
518
581
|
try self.writer.writeAll(&.{
|
|
519
|
-
@
|
|
520
|
-
@
|
|
521
|
-
@
|
|
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
589
|
if (!enabled) return;
|
|
590
|
+
|
|
527
591
|
try self.writer.writeAll(&.{
|
|
528
|
-
@
|
|
529
|
-
@
|
|
530
|
-
@
|
|
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
599
|
if (!enabled) return;
|
|
536
|
-
|
|
600
|
+
|
|
601
|
+
try self.writer.writeAll(&.{@intFromEnum(ArgType.LastStill)});
|
|
537
602
|
}
|
|
538
603
|
|
|
539
604
|
pub fn lastmiss(self: Self) Error!void {
|
|
540
605
|
if (!enabled) return;
|
|
541
|
-
|
|
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(
|
|
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 = @
|
|
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(
|
|
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(
|
|
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 = @
|
|
721
|
+
const reason: Move = @enumFromInt(a[i]);
|
|
648
722
|
printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
|
|
649
|
-
if (reason == .From)
|
|
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(
|
|
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(
|
|
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 = @
|
|
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(
|
|
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 =>
|
|
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(
|
|
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(
|
|
720
|
-
if (reason ==
|
|
721
|
-
id = ID.from(@intCast(
|
|
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 = @
|
|
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(
|
|
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(
|
|
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 = @
|
|
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)
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(@
|
|
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(
|
|
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(@
|
|
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(
|
|
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(
|
|
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(
|
|
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(@
|
|
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(
|
|
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(@
|
|
778
|
-
if (@
|
|
779
|
-
const types = @
|
|
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, @
|
|
782
|
-
formatter(.Type, @
|
|
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 >= @
|
|
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(
|
|
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(@
|
|
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(
|
|
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(@
|
|
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(
|
|
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(
|
|
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
|
|
925
|
+
comptime gen: anytype,
|
|
837
926
|
expected: []const u8,
|
|
838
927
|
actual: []const u8,
|
|
839
928
|
offset: usize,
|
|
840
929
|
) !void {
|
|
841
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(
|
|
855
|
-
format(
|
|
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 @
|
|
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(
|
|
998
|
+
return expectLog(gen1, expected, actual, 0);
|
|
904
999
|
}
|
|
905
1000
|
|
|
906
|
-
fn
|
|
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(@
|
|
909
|
-
.Species => @tagName(@
|
|
910
|
-
.Type => @tagName(@
|
|
911
|
-
.Status =>
|
|
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|" {
|