@pkmn/engine 0.1.0-dev.02b7f810

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 (52) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +332 -0
  3. package/build/pkg/addon.d.ts +7 -0
  4. package/build/pkg/addon.js +111 -0
  5. package/build/pkg/addon.js.map +1 -0
  6. package/build/pkg/data/ids.json +240 -0
  7. package/build/pkg/data/layout.json +86 -0
  8. package/build/pkg/data/protocol.json +173 -0
  9. package/build/pkg/data.d.ts +91 -0
  10. package/build/pkg/data.js +150 -0
  11. package/build/pkg/data.js.map +1 -0
  12. package/build/pkg/gen1.d.ts +109 -0
  13. package/build/pkg/gen1.js +668 -0
  14. package/build/pkg/gen1.js.map +1 -0
  15. package/build/pkg/index.d.ts +383 -0
  16. package/build/pkg/index.js +122 -0
  17. package/build/pkg/index.js.map +1 -0
  18. package/build/pkg/protocol.d.ts +71 -0
  19. package/build/pkg/protocol.js +452 -0
  20. package/build/pkg/protocol.js.map +1 -0
  21. package/build/test/display.js +382 -0
  22. package/build/test/display.js.map +1 -0
  23. package/build/tools/debug.js +133 -0
  24. package/build/tools/debug.js.map +1 -0
  25. package/build.zig +346 -0
  26. package/package.json +202 -0
  27. package/src/bin/install-pkmn-engine +391 -0
  28. package/src/bin/pkmn-debug +62 -0
  29. package/src/lib/binding/node.zig +229 -0
  30. package/src/lib/binding/wasm.zig +10 -0
  31. package/src/lib/common/data.zig +129 -0
  32. package/src/lib/common/debug.zig +64 -0
  33. package/src/lib/common/options.zig +40 -0
  34. package/src/lib/common/protocol.zig +1297 -0
  35. package/src/lib/common/rng.zig +234 -0
  36. package/src/lib/gen1/README.md +791 -0
  37. package/src/lib/gen1/data/moves.zig +1854 -0
  38. package/src/lib/gen1/data/species.zig +1105 -0
  39. package/src/lib/gen1/data/types.zig +131 -0
  40. package/src/lib/gen1/data.zig +620 -0
  41. package/src/lib/gen1/helpers.zig +234 -0
  42. package/src/lib/gen1/mechanics.zig +2819 -0
  43. package/src/lib/gen2/README.md +309 -0
  44. package/src/lib/gen2/data/items.zig +236 -0
  45. package/src/lib/gen2/data/moves.zig +2784 -0
  46. package/src/lib/gen2/data/species.zig +1798 -0
  47. package/src/lib/gen2/data/types.zig +123 -0
  48. package/src/lib/gen2/data.zig +425 -0
  49. package/src/lib/gen2/helpers.zig +225 -0
  50. package/src/lib/gen2/mechanics.zig +53 -0
  51. package/src/lib/pkmn.zig +54 -0
  52. package/src/test/display/pkmn.html.tmpl +368 -0
@@ -0,0 +1,1297 @@
1
+ const std = @import("std");
2
+ const builtin = @import("builtin");
3
+
4
+ const data = @import("./data.zig");
5
+ const options = @import("./options.zig");
6
+
7
+ const assert = std.debug.assert;
8
+ const print = std.debug.print;
9
+
10
+ const trace = options.trace;
11
+
12
+ const Player = data.Player;
13
+ const ID = data.ID;
14
+
15
+ pub const ArgType = enum(u8) {
16
+ None,
17
+
18
+ // Special
19
+ LastStill,
20
+ LastMiss,
21
+
22
+ // Gen 1
23
+ Move,
24
+ Switch,
25
+ Cant,
26
+ Faint,
27
+ Turn,
28
+ Win,
29
+ Tie,
30
+ Damage,
31
+ Heal,
32
+ Status,
33
+ CureStatus,
34
+ Boost,
35
+ ClearAllBoost,
36
+ Fail,
37
+ Miss,
38
+ HitCount,
39
+ Prepare,
40
+ MustRecharge,
41
+ Activate,
42
+ FieldActivate,
43
+ Start,
44
+ End,
45
+ OHKO,
46
+ Crit,
47
+ SuperEffective,
48
+ Resisted,
49
+ Immune,
50
+ Transform,
51
+
52
+ // Gen 2
53
+ Drag,
54
+ Item,
55
+ EndItem,
56
+ CureTeam,
57
+ SetHP,
58
+ SetBoost,
59
+ CopyBoost,
60
+ SideStart,
61
+ SideEnd,
62
+ SingleMove,
63
+ SingleTurn,
64
+ Weather,
65
+
66
+ // Gen 3
67
+ Block,
68
+ Ability,
69
+ EndAbility,
70
+ ClearNegativeBoost,
71
+ FormeChange,
72
+ NoTarget,
73
+
74
+ // Gen 4
75
+ SwapBoost,
76
+ FieldStart,
77
+ FieldEnd,
78
+ DetailsChange,
79
+
80
+ // Gen 5
81
+ ClearPoke,
82
+ Poke,
83
+ TeamPreview,
84
+ Center,
85
+ Swap,
86
+ Combine,
87
+ Waiting,
88
+ Replace,
89
+ ClearBoost,
90
+
91
+ // Gen 6
92
+ Mega,
93
+ Primal,
94
+ InvertBoost,
95
+
96
+ // Gen 7
97
+ ZBroken,
98
+ ZPower,
99
+ Burst,
100
+ ClearPositiveBoost,
101
+
102
+ // Gen 8
103
+ CanDynamax,
104
+ SwapSideConditions,
105
+ };
106
+
107
+ pub const Move = enum(u8) {
108
+ None,
109
+ From,
110
+ };
111
+
112
+ pub const Cant = enum(u8) {
113
+ Sleep,
114
+ Freeze,
115
+ Paralysis,
116
+ Bound,
117
+ Flinch,
118
+ Disable,
119
+ Recharge,
120
+ PP,
121
+ };
122
+
123
+ pub const Heal = enum(u8) {
124
+ None,
125
+ Silent,
126
+ Drain,
127
+ };
128
+
129
+ pub const Damage = enum(u8) {
130
+ None,
131
+ Poison,
132
+ Burn,
133
+ Confusion,
134
+ LeechSeed,
135
+ RecoilOf,
136
+ };
137
+
138
+ pub const Status = enum(u8) {
139
+ None,
140
+ Silent,
141
+ From,
142
+ };
143
+
144
+ pub const CureStatus = enum(u8) {
145
+ Message,
146
+ Silent,
147
+ };
148
+
149
+ pub const Boost = enum(u8) {
150
+ Rage,
151
+ Attack,
152
+ Defense,
153
+ Speed,
154
+ SpecialAttack,
155
+ SpecialDefense,
156
+ Accuracy,
157
+ Evasion,
158
+ };
159
+
160
+ pub const Fail = enum(u8) {
161
+ None,
162
+ Sleep,
163
+ Poison,
164
+ Burn,
165
+ Freeze,
166
+ Paralysis,
167
+ Toxic,
168
+ Substitute,
169
+ Weak,
170
+ };
171
+
172
+ pub const Activate = enum(u8) {
173
+ Bide,
174
+ Confusion,
175
+ Haze,
176
+ Mist,
177
+ Struggle,
178
+ Substitute,
179
+ Splash,
180
+ };
181
+
182
+ pub const Start = enum(u8) {
183
+ Bide,
184
+ Confusion,
185
+ ConfusionSilent,
186
+ FocusEnergy,
187
+ LeechSeed,
188
+ LightScreen,
189
+ Mist,
190
+ Reflect,
191
+ Substitute,
192
+
193
+ TypeChange,
194
+
195
+ Disable,
196
+ Mimic,
197
+ };
198
+
199
+ pub const End = enum(u8) {
200
+ Disable,
201
+ Confusion,
202
+ Bide,
203
+ Substitute,
204
+
205
+ // Silent
206
+ DisableSilent,
207
+ ConfusionSilent,
208
+
209
+ Mist,
210
+ FocusEnergy,
211
+ LeechSeed,
212
+ Toxic,
213
+ LightScreen,
214
+ Reflect,
215
+ };
216
+
217
+ pub const Immune = enum(u8) {
218
+ None,
219
+ OHKO,
220
+ };
221
+
222
+ pub const NULL = Log(@TypeOf(std.io.null_writer)){ .writer = std.io.null_writer };
223
+
224
+ pub fn Log(comptime Writer: type) type {
225
+ return struct {
226
+ const Self = @This();
227
+
228
+ writer: Writer,
229
+
230
+ pub const Error = Writer.Error;
231
+
232
+ pub fn move(self: Self, source: ID, m: anytype, target: ID, from: anytype) Error!void {
233
+ if (!trace) return;
234
+ assert(m != .None);
235
+ try self.writer.writeAll(&.{
236
+ @enumToInt(ArgType.Move),
237
+ @bitCast(u8, source),
238
+ @enumToInt(m),
239
+ @bitCast(u8, target),
240
+ });
241
+ const none = &[_]u8{@enumToInt(Move.None)};
242
+ try self.writer.writeAll(switch (@typeInfo(@TypeOf(from))) {
243
+ .Null => none,
244
+ .Optional => if (from) |f| blk: {
245
+ assert(@enumToInt(f) != 0);
246
+ break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(f) };
247
+ } else none,
248
+ else => blk: {
249
+ assert(@enumToInt(from) != 0);
250
+ break :blk &[_]u8{ @enumToInt(Move.From), @enumToInt(from) };
251
+ },
252
+ });
253
+ }
254
+
255
+ 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 });
259
+ try self.writer.writeIntNative(u16, pokemon.hp);
260
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
261
+ try self.writer.writeAll(&.{pokemon.status});
262
+ }
263
+
264
+ pub fn cant(self: Self, ident: ID, reason: Cant) Error!void {
265
+ if (!trace) return;
266
+ assert(reason != .Disable);
267
+ try self.writer.writeAll(&.{
268
+ @enumToInt(ArgType.Cant),
269
+ @bitCast(u8, ident),
270
+ @enumToInt(reason),
271
+ });
272
+ }
273
+
274
+ pub fn disabled(self: Self, ident: ID, m: anytype) Error!void {
275
+ if (!trace) return;
276
+ try self.writer.writeAll(&.{
277
+ @enumToInt(ArgType.Cant),
278
+ @bitCast(u8, ident),
279
+ @enumToInt(Cant.Disable),
280
+ @enumToInt(m),
281
+ });
282
+ }
283
+
284
+ 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));
288
+ }
289
+
290
+ pub fn turn(self: Self, num: u16) Error!void {
291
+ if (!trace) return;
292
+ try self.writer.writeByte(@enumToInt(ArgType.Turn));
293
+ try self.writer.writeIntNative(u16, num);
294
+ try self.writer.writeByte(@enumToInt(ArgType.None));
295
+ }
296
+
297
+ pub fn win(self: Self, player: Player) Error!void {
298
+ if (!trace) return;
299
+ try self.writer.writeAll(&.{
300
+ @enumToInt(ArgType.Win),
301
+ @enumToInt(player),
302
+ @enumToInt(ArgType.None),
303
+ });
304
+ }
305
+
306
+ pub fn tie(self: Self) Error!void {
307
+ if (!trace) return;
308
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Tie), @enumToInt(ArgType.None) });
309
+ }
310
+
311
+ 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) });
315
+ try self.writer.writeIntNative(u16, pokemon.hp);
316
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
317
+ try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
318
+ }
319
+
320
+ pub fn damageOf(
321
+ self: Self,
322
+ ident: ID,
323
+ pokemon: anytype,
324
+ reason: Damage,
325
+ source: ID,
326
+ ) Error!void {
327
+ if (!trace) return;
328
+ assert(@enumToInt(reason) == @enumToInt(Damage.RecoilOf));
329
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Damage), @bitCast(u8, ident) });
330
+ try self.writer.writeIntNative(u16, pokemon.hp);
331
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
332
+ try self.writer.writeAll(&.{
333
+ pokemon.status,
334
+ @enumToInt(reason),
335
+ @bitCast(u8, source),
336
+ });
337
+ }
338
+
339
+ pub fn heal(self: Self, ident: ID, pokemon: anytype, reason: Heal) Error!void {
340
+ if (!trace) return;
341
+ assert(reason != .Drain);
342
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Heal), @bitCast(u8, ident) });
343
+ try self.writer.writeIntNative(u16, pokemon.hp);
344
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
345
+ try self.writer.writeAll(&.{ pokemon.status, @enumToInt(reason) });
346
+ }
347
+
348
+ 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) });
351
+ try self.writer.writeIntNative(u16, pokemon.hp);
352
+ try self.writer.writeIntNative(u16, pokemon.stats.hp);
353
+ try self.writer.writeAll(&.{
354
+ pokemon.status,
355
+ @enumToInt(Heal.Drain),
356
+ @bitCast(u8, target),
357
+ });
358
+ }
359
+
360
+ pub fn status(self: Self, ident: ID, value: u8, reason: Status) Error!void {
361
+ if (!trace) return;
362
+ assert(reason != .From);
363
+ try self.writer.writeAll(&.{
364
+ @enumToInt(ArgType.Status),
365
+ @bitCast(u8, ident),
366
+ value,
367
+ @enumToInt(reason),
368
+ });
369
+ }
370
+
371
+ pub fn statusFrom(self: Self, ident: ID, value: u8, m: anytype) Error!void {
372
+ if (!trace) return;
373
+ try self.writer.writeAll(&.{
374
+ @enumToInt(ArgType.Status),
375
+ @bitCast(u8, ident),
376
+ value,
377
+ @enumToInt(Status.From),
378
+ @enumToInt(m),
379
+ });
380
+ }
381
+
382
+ pub fn curestatus(self: Self, ident: ID, value: u8, reason: CureStatus) Error!void {
383
+ if (!trace) return;
384
+ try self.writer.writeAll(&.{
385
+ @enumToInt(ArgType.CureStatus),
386
+ @bitCast(u8, ident),
387
+ value,
388
+ @enumToInt(reason),
389
+ });
390
+ }
391
+
392
+ pub fn boost(self: Self, ident: ID, reason: Boost, num: i8) Error!void {
393
+ if (!trace) return;
394
+ assert(num != 0 and (reason != .Rage or num > 0));
395
+ try self.writer.writeAll(&.{
396
+ @enumToInt(ArgType.Boost),
397
+ @bitCast(u8, ident),
398
+ @enumToInt(reason),
399
+ @intCast(u8, num + 6),
400
+ });
401
+ }
402
+
403
+ pub fn clearallboost(self: Self) Error!void {
404
+ if (!trace) return;
405
+ try self.writer.writeAll(&.{@enumToInt(ArgType.ClearAllBoost)});
406
+ }
407
+
408
+ pub fn fail(self: Self, ident: ID, reason: Fail) Error!void {
409
+ if (!trace) return;
410
+ try self.writer.writeAll(&.{
411
+ @enumToInt(ArgType.Fail),
412
+ @bitCast(u8, ident),
413
+ @enumToInt(reason),
414
+ });
415
+ }
416
+
417
+ pub fn miss(self: Self, source: ID) Error!void {
418
+ if (!trace) return;
419
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Miss), @bitCast(u8, source) });
420
+ }
421
+
422
+ 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 });
425
+ }
426
+
427
+ pub fn prepare(self: Self, source: ID, m: anytype) Error!void {
428
+ if (!trace) return;
429
+ try self.writer.writeAll(&.{
430
+ @enumToInt(ArgType.Prepare),
431
+ @bitCast(u8, source),
432
+ @enumToInt(m),
433
+ });
434
+ }
435
+
436
+ pub fn mustrecharge(self: Self, ident: ID) Error!void {
437
+ if (!trace) return;
438
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.MustRecharge), @bitCast(u8, ident) });
439
+ }
440
+
441
+ pub fn activate(self: Self, ident: ID, reason: Activate) Error!void {
442
+ if (!trace) return;
443
+ try self.writer.writeAll(&.{
444
+ @enumToInt(ArgType.Activate),
445
+ @bitCast(u8, ident),
446
+ @enumToInt(reason),
447
+ });
448
+ }
449
+
450
+ pub fn fieldactivate(self: Self) Error!void {
451
+ if (!trace) return;
452
+ try self.writer.writeAll(&.{@enumToInt(ArgType.FieldActivate)});
453
+ }
454
+
455
+ pub fn start(self: Self, ident: ID, reason: Start) Error!void {
456
+ if (!trace) return;
457
+ assert(@enumToInt(reason) < @enumToInt(Start.TypeChange));
458
+ try self.writer.writeAll(&.{
459
+ @enumToInt(ArgType.Start),
460
+ @bitCast(u8, ident),
461
+ @enumToInt(reason),
462
+ });
463
+ }
464
+
465
+ pub fn typechange(self: Self, source: ID, types: anytype, target: ID) Error!void {
466
+ if (!trace) return;
467
+ try self.writer.writeAll(&.{
468
+ @enumToInt(ArgType.Start),
469
+ @bitCast(u8, source),
470
+ @enumToInt(Start.TypeChange),
471
+ @bitCast(u8, types),
472
+ @bitCast(u8, target),
473
+ });
474
+ }
475
+
476
+ pub fn startEffect(self: Self, ident: ID, reason: Start, m: anytype) Error!void {
477
+ if (!trace) return;
478
+ assert(@enumToInt(reason) > @enumToInt(Start.TypeChange));
479
+ try self.writer.writeAll(&.{
480
+ @enumToInt(ArgType.Start),
481
+ @bitCast(u8, ident),
482
+ @enumToInt(reason),
483
+ @enumToInt(m),
484
+ });
485
+ }
486
+
487
+ pub fn end(self: Self, ident: ID, reason: End) Error!void {
488
+ if (!trace) return;
489
+ try self.writer.writeAll(&.{
490
+ @enumToInt(ArgType.End),
491
+ @bitCast(u8, ident),
492
+ @enumToInt(reason),
493
+ });
494
+ }
495
+
496
+ pub fn ohko(self: Self) Error!void {
497
+ if (!trace) return;
498
+ try self.writer.writeAll(&.{@enumToInt(ArgType.OHKO)});
499
+ }
500
+
501
+ pub fn crit(self: Self, ident: ID) Error!void {
502
+ if (!trace) return;
503
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Crit), @bitCast(u8, ident) });
504
+ }
505
+
506
+ pub fn supereffective(self: Self, ident: ID) Error!void {
507
+ if (!trace) return;
508
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.SuperEffective), @bitCast(u8, ident) });
509
+ }
510
+
511
+ pub fn resisted(self: Self, ident: ID) Error!void {
512
+ if (!trace) return;
513
+ try self.writer.writeAll(&.{ @enumToInt(ArgType.Resisted), @bitCast(u8, ident) });
514
+ }
515
+
516
+ pub fn immune(self: Self, ident: ID, reason: Immune) Error!void {
517
+ if (!trace) return;
518
+ try self.writer.writeAll(&.{
519
+ @enumToInt(ArgType.Immune),
520
+ @bitCast(u8, ident),
521
+ @enumToInt(reason),
522
+ });
523
+ }
524
+
525
+ pub fn transform(self: Self, source: ID, target: ID) Error!void {
526
+ if (!trace) return;
527
+ try self.writer.writeAll(&.{
528
+ @enumToInt(ArgType.Transform),
529
+ @bitCast(u8, source),
530
+ @bitCast(u8, target),
531
+ });
532
+ }
533
+
534
+ pub fn laststill(self: Self) Error!void {
535
+ if (!trace) return;
536
+ try self.writer.writeAll(&.{@enumToInt(ArgType.LastStill)});
537
+ }
538
+
539
+ pub fn lastmiss(self: Self) Error!void {
540
+ if (!trace) return;
541
+ try self.writer.writeAll(&.{@enumToInt(ArgType.LastMiss)});
542
+ }
543
+ };
544
+ }
545
+
546
+ pub const FixedLog = Log(ByteStream.Writer);
547
+
548
+ pub const ByteStream = struct {
549
+ buffer: []u8,
550
+ pos: usize = 0,
551
+
552
+ pub const Writer = struct {
553
+ stream: *ByteStream,
554
+
555
+ pub const Error = error{NoSpaceLeft};
556
+
557
+ pub fn writeAll(self: Writer, bytes: []const u8) Error!void {
558
+ for (bytes) |b| try self.writeByte(b);
559
+ }
560
+
561
+ pub fn writeByte(self: Writer, byte: u8) Error!void {
562
+ try self.stream.writeByte(byte);
563
+ }
564
+
565
+ pub fn writeIntNative(self: Writer, comptime T: type, value: T) Error!void {
566
+ // TODO: rework this to write directly to the buffer?
567
+ var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
568
+ std.mem.writeIntNative(T, &bytes, value);
569
+ return self.writeAll(&bytes);
570
+ }
571
+ };
572
+
573
+ pub fn writer(self: *ByteStream) Writer {
574
+ return .{ .stream = self };
575
+ }
576
+
577
+ pub fn writeByte(self: *ByteStream, byte: u8) Writer.Error!void {
578
+ if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
579
+ self.buffer[self.pos] = byte;
580
+ self.pos += 1;
581
+ }
582
+
583
+ pub fn reset(self: *ByteStream) void {
584
+ self.pos = 0;
585
+ }
586
+ };
587
+
588
+ pub const Kind = enum { Move, Species, Type, Status };
589
+ pub const Formatter = fn (Kind, u8) []const u8;
590
+
591
+ pub fn format(comptime formatter: Formatter, a: []const u8, b: ?[]const u8, color: bool) void {
592
+ print("\n", .{});
593
+
594
+ var i: usize = 0;
595
+ while (i < a.len) {
596
+ const arg = @intToEnum(ArgType, a[i]);
597
+ const name = switch (arg) {
598
+ .None => if (color) "\x1b[2m-\x1b[0m" else "-",
599
+ .LastStill => "|[still]",
600
+ .LastMiss => "|[miss]",
601
+ .Move => "|move|",
602
+ .Switch => "|switch|",
603
+ .Cant => "|cant|",
604
+ .Faint => "|faint|",
605
+ .Turn => "|turn|",
606
+ .Win => "|win|",
607
+ .Tie => "|tie|",
608
+ .Damage => "|-damage|",
609
+ .Heal => "|-heal|",
610
+ .Status => "|-status|",
611
+ .CureStatus => "|-curestatus|",
612
+ .Boost => "|-boost|",
613
+ .ClearAllBoost => "|-clearallboost|",
614
+ .Fail => "|-fail|",
615
+ .Miss => "|-miss|",
616
+ .HitCount => "|-hitcount|",
617
+ .Prepare => "|-prepare|",
618
+ .MustRecharge => "|-mustrecharge|",
619
+ .Activate => "|-activate|",
620
+ .FieldActivate => "|-fieldactivate|",
621
+ .Start => "|-start|",
622
+ .End => "|-end|",
623
+ .OHKO => "|-ohko|",
624
+ .Crit => "|-crit|",
625
+ .SuperEffective => "|-supereffective|",
626
+ .Resisted => "|-resisted|",
627
+ .Immune => "|-immune|",
628
+ .Transform => "|-transform|",
629
+ else => unreachable,
630
+ };
631
+ printc("{s}", .{name}, a, b, &i, 1, color);
632
+ switch (arg) {
633
+ .None,
634
+ .LastStill,
635
+ .LastMiss,
636
+ .Tie,
637
+ .ClearAllBoost,
638
+ .FieldActivate,
639
+ .OHKO,
640
+ => {},
641
+ .Move => {
642
+ const source = ID.from(@intCast(u4, a[i]));
643
+ 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]));
646
+ printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
647
+ const reason = @intToEnum(Move, a[i]);
648
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
649
+ if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
650
+ },
651
+ .Switch => {
652
+ const id = ID.from(@intCast(u4, a[i]));
653
+ 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);
655
+ printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
656
+ switch (endian) {
657
+ .Big => {
658
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
659
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
660
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
661
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
662
+ },
663
+ .Little => {
664
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
665
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
666
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
667
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
668
+ },
669
+ }
670
+ printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
671
+ },
672
+ .Cant => {
673
+ const id = ID.from(@intCast(u4, a[i]));
674
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
675
+ const reason = @intToEnum(Cant, a[i]);
676
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
677
+ if (reason == .Disable) {
678
+ printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
679
+ }
680
+ },
681
+ .Faint,
682
+ .Miss,
683
+ .MustRecharge,
684
+ .Crit,
685
+ .SuperEffective,
686
+ .Resisted,
687
+ => {
688
+ const id = ID.from(@intCast(u4, a[i]));
689
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
690
+ },
691
+ .Turn => {
692
+ const turn = switch (endian) {
693
+ .Big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
694
+ .Little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
695
+ };
696
+ printc(" {d}", .{turn}, a, b, &i, 2, color);
697
+ },
698
+ .Win => printc(" {s}", .{@tagName(@intToEnum(Player, a[i]))}, a, b, &i, 1, color),
699
+ .Damage, .Heal => {
700
+ var id = ID.from(@intCast(u4, a[i]));
701
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
702
+ switch (endian) {
703
+ .Big => {
704
+ var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
705
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
706
+ hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
707
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
708
+ },
709
+ .Little => {
710
+ var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
711
+ printc(" {d}", .{hp}, a, b, &i, 2, color);
712
+ hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
713
+ printc("/{d}", .{hp}, a, b, &i, 2, color);
714
+ },
715
+ }
716
+ printc(" {s}", .{formatter(.Status, a[i])}, a, b, &i, 1, color);
717
+ 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]));
722
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
723
+ }
724
+ } else {
725
+ const reason = @intToEnum(Heal, a[i]);
726
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
727
+ if (reason == .Drain) {
728
+ id = ID.from(@intCast(u4, a[i]));
729
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
730
+ }
731
+ }
732
+ },
733
+ .Status => {
734
+ const id = ID.from(@intCast(u4, a[i]));
735
+ 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]);
738
+ printc(" {s}", .{@tagName(reason)}, a, b, &i, 1, color);
739
+ if (reason == .From) printc(" {s}", .{formatter(.Move, a[i])}, a, b, &i, 1, color);
740
+ },
741
+ .CureStatus => {
742
+ const id = ID.from(@intCast(u4, a[i]));
743
+ 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);
746
+ },
747
+ .Boost => {
748
+ const id = ID.from(@intCast(u4, a[i]));
749
+ 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);
751
+ printc(" {d}", .{a[i]}, a, b, &i, 1, color);
752
+ },
753
+ .Fail => {
754
+ const id = ID.from(@intCast(u4, a[i]));
755
+ 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);
757
+ },
758
+ .HitCount => {
759
+ const id = ID.from(@intCast(u4, a[i]));
760
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
761
+ printc(" {d}", .{a[i]}, a, b, &i, 1, color);
762
+ },
763
+ .Prepare => {
764
+ const id = ID.from(@intCast(u4, a[i]));
765
+ 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);
767
+ },
768
+ .Activate => {
769
+ const id = ID.from(@intCast(u4, a[i]));
770
+ 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);
772
+ },
773
+ .Start => {
774
+ const id = ID.from(@intCast(u4, a[i]));
775
+ printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
776
+ 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]);
780
+ const args = .{
781
+ formatter(.Type, @enumToInt(types.type1)),
782
+ formatter(.Type, @enumToInt(types.type2)),
783
+ };
784
+ 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);
787
+ }
788
+ },
789
+ .End => {
790
+ const id = ID.from(@intCast(u4, a[i]));
791
+ 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);
793
+ },
794
+ .Immune => {
795
+ const id = ID.from(@intCast(u4, a[i]));
796
+ 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);
798
+ },
799
+ .Transform => {
800
+ const source = ID.from(@intCast(u4, a[i]));
801
+ printc(" {s}({d})", .{ @tagName(source.player), source.id }, a, b, &i, 1, color);
802
+ const target = ID.from(@intCast(u4, a[i]));
803
+ printc(" {s}({d})", .{ @tagName(target.player), target.id }, a, b, &i, 1, color);
804
+ },
805
+ else => unreachable,
806
+ }
807
+ print("\n", .{});
808
+ }
809
+
810
+ print("\n", .{});
811
+ }
812
+
813
+ fn printc(
814
+ comptime fmt: []const u8,
815
+ args: anytype,
816
+ a: []const u8,
817
+ b: ?[]const u8,
818
+ i: *usize,
819
+ n: usize,
820
+ color: bool,
821
+ ) void {
822
+ const c = color and (if (b) |x| mismatch: {
823
+ const end = i.* + n;
824
+ if (end > a.len or end > x.len) break :mismatch true;
825
+ var j: usize = i.*;
826
+ while (j < end) : (j += 1) if (a[j] != x[j]) break :mismatch true;
827
+ break :mismatch false;
828
+ } else false);
829
+ if (c) print("\x1b[31m", .{});
830
+ print(fmt, args);
831
+ if (c) print("\x1b[0m", .{});
832
+ i.* += n;
833
+ }
834
+
835
+ pub fn expectLog(
836
+ comptime formatter: Formatter,
837
+ expected: []const u8,
838
+ actual: []const u8,
839
+ offset: usize,
840
+ ) !void {
841
+ if (!trace) return;
842
+ const color = color: {
843
+ if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
844
+ break :color true;
845
+ } else if (std.process.hasEnvVarConstant("NO_COLOR")) {
846
+ break :color false;
847
+ } else {
848
+ break :color std.io.getStdErr().supportsAnsiEscapeCodes();
849
+ }
850
+ };
851
+
852
+ expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
853
+ error.TestExpectedEqual => {
854
+ format(formatter, expected, null, color);
855
+ format(formatter, actual, expected, color);
856
+ return err;
857
+ },
858
+ else => return err,
859
+ };
860
+ }
861
+
862
+ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !void {
863
+ for (offset..@min(expected.len, actual.len)) |i| {
864
+ if (expected[i] != actual[i]) {
865
+ print(
866
+ "index {} incorrect. expected 0x{X:0>2}, found 0x{X:0>2}\n",
867
+ .{ i, expected[i], actual[i] },
868
+ );
869
+ return error.TestExpectedEqual;
870
+ }
871
+ }
872
+ if (expected.len != actual.len) {
873
+ print(
874
+ "slice lengths differ. expected {d}, found {d}\n",
875
+ .{ expected.len, actual.len },
876
+ );
877
+ return error.TestExpectedEqual;
878
+ }
879
+ }
880
+
881
+ const endian = builtin.target.cpu.arch.endian();
882
+
883
+ fn N(e: anytype) u8 {
884
+ return @enumToInt(e);
885
+ }
886
+
887
+ const p1 = Player.P1;
888
+ const p2 = Player.P2;
889
+
890
+ const gen1 = struct {
891
+ pub usingnamespace @import("../gen1/data.zig");
892
+ pub const helpers = @import("../gen1/helpers.zig");
893
+ };
894
+
895
+ var buf: [gen1.LOGS_SIZE]u8 = undefined;
896
+ var stream = ByteStream{ .buffer = &buf };
897
+ var log: FixedLog = .{ .writer = stream.writer() };
898
+
899
+ const M = gen1.Move;
900
+ const S = gen1.Species;
901
+
902
+ fn expectLog1(expected: []const u8, actual: []const u8) !void {
903
+ return expectLog(gen1Formatter, expected, actual, 0);
904
+ }
905
+
906
+ fn gen1Formatter(kind: Kind, byte: u8) []const u8 {
907
+ 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),
912
+ };
913
+ }
914
+
915
+ test "|move|" {
916
+ try log.move(p2.ident(4), M.Thunderbolt, p1.ident(5), null);
917
+ try expectLog1(
918
+ &.{ N(ArgType.Move), 0b1100, N(M.Thunderbolt), 0b0101, N(Move.None) },
919
+ buf[0..5],
920
+ );
921
+ stream.reset();
922
+
923
+ try log.move(p2.ident(4), M.Wrap, p1.ident(5), M.Wrap);
924
+ const wrap = N(M.Wrap);
925
+ try expectLog1(
926
+ &.{ N(ArgType.Move), 0b1100, wrap, 0b0101, N(Move.From), wrap },
927
+ buf[0..6],
928
+ );
929
+ stream.reset();
930
+
931
+ try log.move(p2.ident(4), M.SkullBash, .{}, null);
932
+ try log.laststill();
933
+ try expectLog1(
934
+ &.{
935
+ N(ArgType.Move),
936
+ 0b1100,
937
+ N(M.SkullBash),
938
+ 0,
939
+ N(Move.None),
940
+ N(ArgType.LastStill),
941
+ },
942
+ buf[0..6],
943
+ );
944
+ stream.reset();
945
+
946
+ try log.move(p2.ident(4), M.Tackle, p1.ident(5), null);
947
+ try log.lastmiss();
948
+ try expectLog1(
949
+ &.{
950
+ N(ArgType.Move),
951
+ 0b1100,
952
+ N(M.Tackle),
953
+ 0b0101,
954
+ N(Move.None),
955
+ N(ArgType.LastMiss),
956
+ },
957
+ buf[0..6],
958
+ );
959
+ stream.reset();
960
+ }
961
+
962
+ test "|switch|" {
963
+ var snorlax = gen1.helpers.Pokemon.init(.{ .species = .Snorlax, .moves = &.{.Splash} });
964
+ snorlax.level = 91;
965
+ snorlax.hp = 200;
966
+ snorlax.stats.hp = 400;
967
+ snorlax.status = gen1.Status.init(.PAR);
968
+ try log.switched(p2.ident(3), &snorlax);
969
+ const par = 0b1000000;
970
+ var expected: []const u8 = switch (endian) {
971
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 0, 200, 1, 144, par },
972
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 91, 200, 0, 144, 1, par },
973
+ };
974
+ try expectLog1(expected, buf[0..9]);
975
+ stream.reset();
976
+
977
+ snorlax.level = 100;
978
+ snorlax.hp = 0;
979
+ snorlax.status = 0;
980
+ try log.switched(p2.ident(3), &snorlax);
981
+ expected = switch (endian) {
982
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 1, 144, 0 },
983
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 0, 0, 144, 1, 0 },
984
+ };
985
+ try expectLog1(expected, buf[0..9]);
986
+ stream.reset();
987
+
988
+ snorlax.hp = 400;
989
+ try log.switched(p2.ident(3), &snorlax);
990
+ expected = switch (endian) {
991
+ .Big => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 1, 144, 1, 144, 0 },
992
+ .Little => &.{ N(ArgType.Switch), 0b1011, N(S.Snorlax), 100, 144, 1, 144, 1, 0 },
993
+ };
994
+ try expectLog1(expected, buf[0..9]);
995
+ stream.reset();
996
+ }
997
+
998
+ test "|cant|" {
999
+ try log.cant(p2.ident(6), .Bound);
1000
+ try expectLog1(&.{ N(ArgType.Cant), 0b1110, N(Cant.Bound) }, buf[0..3]);
1001
+ stream.reset();
1002
+
1003
+ try log.disabled(p1.ident(2), M.Earthquake);
1004
+ try expectLog1(&.{ N(ArgType.Cant), 2, N(Cant.Disable), N(M.Earthquake) }, buf[0..4]);
1005
+ stream.reset();
1006
+ }
1007
+
1008
+ test "|faint|" {
1009
+ try log.faint(p2.ident(2), false);
1010
+ try expectLog1(&.{ N(ArgType.Faint), 0b1010 }, buf[0..2]);
1011
+ stream.reset();
1012
+
1013
+ try log.faint(p2.ident(2), true);
1014
+ try expectLog1(&.{ N(ArgType.Faint), 0b1010, N(ArgType.None) }, buf[0..3]);
1015
+ stream.reset();
1016
+ }
1017
+
1018
+ test "|turn|" {
1019
+ try log.turn(42);
1020
+ var expected = switch (endian) {
1021
+ .Big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1022
+ .Little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1023
+ };
1024
+ try expectLog1(expected, buf[0..4]);
1025
+ stream.reset();
1026
+ }
1027
+
1028
+ test "|win|" {
1029
+ try log.win(.P2);
1030
+ try expectLog1(&.{ N(ArgType.Win), 1, N(ArgType.None) }, buf[0..3]);
1031
+ stream.reset();
1032
+ }
1033
+
1034
+ test "|tie|" {
1035
+ try log.tie();
1036
+ try expectLog1(&.{ N(ArgType.Tie), N(ArgType.None) }, buf[0..2]);
1037
+ stream.reset();
1038
+ }
1039
+
1040
+ test "|-damage|" {
1041
+ var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1042
+ chansey.hp = 612;
1043
+ chansey.status = gen1.Status.slp(1);
1044
+ try log.damage(p2.ident(2), &chansey, .None);
1045
+ var expected: []const u8 = switch (endian) {
1046
+ .Big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1047
+ .Little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1048
+ };
1049
+ try expectLog1(expected, buf[0..8]);
1050
+ stream.reset();
1051
+
1052
+ chansey.hp = 100;
1053
+ chansey.stats.hp = 256;
1054
+ chansey.status = 0;
1055
+ try log.damage(p2.ident(2), &chansey, .Confusion);
1056
+ expected = switch (endian) {
1057
+ .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1058
+ .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1059
+ };
1060
+ try expectLog1(expected, buf[0..8]);
1061
+ stream.reset();
1062
+
1063
+ chansey.status = gen1.Status.init(.PSN);
1064
+ try log.damageOf(p2.ident(2), &chansey, .RecoilOf, p1.ident(1));
1065
+ expected = switch (endian) {
1066
+ .Big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1067
+ .Little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1068
+ };
1069
+ try expectLog1(expected, buf[0..9]);
1070
+ stream.reset();
1071
+ }
1072
+
1073
+ test "|-heal|" {
1074
+ var chansey = gen1.helpers.Pokemon.init(.{ .species = .Chansey, .moves = &.{.Splash} });
1075
+ chansey.hp = 612;
1076
+ chansey.status = gen1.Status.slp(1);
1077
+ try log.heal(p2.ident(2), &chansey, .None);
1078
+ var expected: []const u8 = switch (endian) {
1079
+ .Big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1080
+ .Little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1081
+ };
1082
+ try expectLog1(expected, buf[0..8]);
1083
+ stream.reset();
1084
+
1085
+ chansey.hp = 100;
1086
+ chansey.stats.hp = 256;
1087
+ chansey.status = 0;
1088
+ try log.heal(p2.ident(2), &chansey, .Silent);
1089
+ expected = switch (endian) {
1090
+ .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1091
+ .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1092
+ };
1093
+ try expectLog1(expected, buf[0..8]);
1094
+ stream.reset();
1095
+
1096
+ try log.drain(p2.ident(2), &chansey, p1.ident(1));
1097
+ expected = switch (endian) {
1098
+ .Big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1099
+ .Little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1100
+ };
1101
+ try expectLog1(expected, buf[0..9]);
1102
+ stream.reset();
1103
+ }
1104
+
1105
+ test "|-status|" {
1106
+ try log.status(p2.ident(6), gen1.Status.init(.BRN), .None);
1107
+ try expectLog1(&.{ N(ArgType.Status), 0b1110, 0b10000, N(Status.None) }, buf[0..4]);
1108
+ stream.reset();
1109
+
1110
+ try log.status(p2.ident(2), gen1.Status.init(.PSN), .Silent);
1111
+ try expectLog1(&.{ N(ArgType.Status), 0b1010, 0b1000, N(Status.Silent) }, buf[0..4]);
1112
+ stream.reset();
1113
+
1114
+ try log.statusFrom(p1.ident(1), gen1.Status.init(.PAR), M.BodySlam);
1115
+ try expectLog1(
1116
+ &.{ N(ArgType.Status), 0b0001, 0b1000000, N(Status.From), N(M.BodySlam) },
1117
+ buf[0..5],
1118
+ );
1119
+ stream.reset();
1120
+ }
1121
+
1122
+ test "|-curestatus|" {
1123
+ try log.curestatus(p2.ident(6), gen1.Status.slp(7), .Message);
1124
+ try expectLog1(&.{ N(ArgType.CureStatus), 0b1110, 0b111, N(CureStatus.Message) }, buf[0..4]);
1125
+ stream.reset();
1126
+
1127
+ try log.curestatus(p1.ident(2), gen1.Status.TOX, .Silent);
1128
+ try expectLog1(&.{
1129
+ N(ArgType.CureStatus),
1130
+ 0b0010,
1131
+ 0b10001000,
1132
+ N(CureStatus.Silent),
1133
+ }, buf[0..4]);
1134
+ stream.reset();
1135
+ }
1136
+
1137
+ test "|-boost|" {
1138
+ try log.boost(p2.ident(6), .Speed, 2);
1139
+ try expectLog1(&.{ N(ArgType.Boost), 0b1110, N(Boost.Speed), 8 }, buf[0..4]);
1140
+ stream.reset();
1141
+
1142
+ try log.boost(p1.ident(2), .Rage, 1);
1143
+ try expectLog1(&.{ N(ArgType.Boost), 0b0010, N(Boost.Rage), 7 }, buf[0..4]);
1144
+ stream.reset();
1145
+
1146
+ try log.boost(p2.ident(3), .Defense, -2);
1147
+ try expectLog1(&.{ N(ArgType.Boost), 0b1011, N(Boost.Defense), 4 }, buf[0..4]);
1148
+ stream.reset();
1149
+ }
1150
+
1151
+ test "|-clearallboost|" {
1152
+ try log.clearallboost();
1153
+ try expectLog1(&.{N(ArgType.ClearAllBoost)}, buf[0..1]);
1154
+ stream.reset();
1155
+ }
1156
+
1157
+ test "|-fail|" {
1158
+ try log.fail(p2.ident(6), .None);
1159
+ try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.None) }, buf[0..3]);
1160
+ stream.reset();
1161
+
1162
+ try log.fail(p2.ident(6), .Sleep);
1163
+ try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Sleep) }, buf[0..3]);
1164
+ stream.reset();
1165
+
1166
+ try log.fail(p2.ident(6), .Substitute);
1167
+ try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Substitute) }, buf[0..3]);
1168
+ stream.reset();
1169
+
1170
+ try log.fail(p2.ident(6), .Weak);
1171
+ try expectLog1(&.{ N(ArgType.Fail), 0b1110, N(Fail.Weak) }, buf[0..3]);
1172
+ stream.reset();
1173
+ }
1174
+
1175
+ test "|-miss|" {
1176
+ try log.miss(p2.ident(4));
1177
+ try expectLog1(&.{ N(ArgType.Miss), 0b1100 }, buf[0..2]);
1178
+ stream.reset();
1179
+ }
1180
+ test "|-hitcount|" {
1181
+ try log.hitcount(p2.ident(1), 5);
1182
+ try expectLog1(&.{ N(ArgType.HitCount), 0b1001, 5 }, buf[0..3]);
1183
+ stream.reset();
1184
+ }
1185
+
1186
+ test "|-prepare|" {
1187
+ try log.prepare(p2.ident(2), M.Dig);
1188
+ try expectLog1(&.{ N(ArgType.Prepare), 0b1010, N(M.Dig) }, buf[0..3]);
1189
+ stream.reset();
1190
+ }
1191
+
1192
+ test "|-mustrecharge|" {
1193
+ try log.mustrecharge(p1.ident(6));
1194
+ try expectLog1(&.{ N(ArgType.MustRecharge), 0b0110 }, buf[0..2]);
1195
+ stream.reset();
1196
+ }
1197
+
1198
+ test "|-activate|" {
1199
+ try log.activate(p1.ident(2), .Struggle);
1200
+ try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Struggle) }, buf[0..3]);
1201
+ stream.reset();
1202
+
1203
+ try log.activate(p2.ident(4), .Mist);
1204
+ try expectLog1(&.{ N(ArgType.Activate), 0b1100, N(Activate.Mist) }, buf[0..3]);
1205
+ stream.reset();
1206
+
1207
+ try log.activate(p2.ident(6), .Substitute);
1208
+ try expectLog1(&.{ N(ArgType.Activate), 0b1110, N(Activate.Substitute) }, buf[0..3]);
1209
+ stream.reset();
1210
+
1211
+ try log.activate(p1.ident(2), .Splash);
1212
+ try expectLog1(&.{ N(ArgType.Activate), 0b0010, N(Activate.Splash) }, buf[0..3]);
1213
+ stream.reset();
1214
+ }
1215
+
1216
+ test "|-fieldactivate|" {
1217
+ try log.fieldactivate();
1218
+ try expectLog1(&.{N(ArgType.FieldActivate)}, buf[0..1]);
1219
+ stream.reset();
1220
+ }
1221
+
1222
+ test "|-start|" {
1223
+ try log.start(p2.ident(6), .Bide);
1224
+ try expectLog1(&.{ N(ArgType.Start), 0b1110, N(Start.Bide) }, buf[0..3]);
1225
+ stream.reset();
1226
+
1227
+ try log.start(p1.ident(2), .ConfusionSilent);
1228
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.ConfusionSilent) }, buf[0..3]);
1229
+ stream.reset();
1230
+
1231
+ try log.typechange(p2.ident(6), gen1.Types{ .type1 = .Fire, .type2 = .Fire }, p2.ident(5));
1232
+ try expectLog1(
1233
+ &.{ N(ArgType.Start), 0b1110, N(Start.TypeChange), 0b1000_1000, 0b1101 },
1234
+ buf[0..5],
1235
+ );
1236
+ stream.reset();
1237
+
1238
+ try log.typechange(p1.ident(2), gen1.Types{ .type1 = .Bug, .type2 = .Poison }, p2.ident(4));
1239
+ try expectLog1(
1240
+ &.{ N(ArgType.Start), 0b0010, N(Start.TypeChange), 0b0011_0110, 0b1100 },
1241
+ buf[0..5],
1242
+ );
1243
+ stream.reset();
1244
+
1245
+ try log.startEffect(p1.ident(2), .Disable, M.Surf);
1246
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Disable), N(M.Surf) }, buf[0..4]);
1247
+ stream.reset();
1248
+
1249
+ try log.startEffect(p1.ident(2), .Mimic, M.Surf);
1250
+ try expectLog1(&.{ N(ArgType.Start), 0b0010, N(Start.Mimic), N(M.Surf) }, buf[0..4]);
1251
+ stream.reset();
1252
+ }
1253
+
1254
+ test "|-end|" {
1255
+ try log.end(p2.ident(6), .Bide);
1256
+ try expectLog1(&.{ N(ArgType.End), 0b1110, N(End.Bide) }, buf[0..3]);
1257
+ stream.reset();
1258
+
1259
+ try log.end(p1.ident(2), .ConfusionSilent);
1260
+ try expectLog1(&.{ N(ArgType.End), 0b0010, N(End.ConfusionSilent) }, buf[0..3]);
1261
+ stream.reset();
1262
+ }
1263
+
1264
+ test "|-ohko|" {
1265
+ try log.ohko();
1266
+ try expectLog1(&.{N(ArgType.OHKO)}, buf[0..1]);
1267
+ stream.reset();
1268
+ }
1269
+ test "|-crit|" {
1270
+ try log.crit(p2.ident(5));
1271
+ try expectLog1(&.{ N(ArgType.Crit), 0b1101 }, buf[0..2]);
1272
+ stream.reset();
1273
+ }
1274
+ test "|-supereffective|" {
1275
+ try log.supereffective(p1.ident(1));
1276
+ try expectLog1(&.{ N(ArgType.SuperEffective), 0b0001 }, buf[0..2]);
1277
+ stream.reset();
1278
+ }
1279
+ test "|-resisted|" {
1280
+ try log.resisted(p2.ident(2));
1281
+ try expectLog1(&.{ N(ArgType.Resisted), 0b1010 }, buf[0..2]);
1282
+ stream.reset();
1283
+ }
1284
+ test "|-immune|" {
1285
+ try log.immune(p1.ident(3), .None);
1286
+ try expectLog1(&.{ N(ArgType.Immune), 0b0011, N(Immune.None) }, buf[0..3]);
1287
+ stream.reset();
1288
+
1289
+ try log.immune(p2.ident(2), .OHKO);
1290
+ try expectLog1(&.{ N(ArgType.Immune), 0b1010, N(Immune.OHKO) }, buf[0..3]);
1291
+ stream.reset();
1292
+ }
1293
+ test "|-transform|" {
1294
+ try log.transform(p2.ident(4), p1.ident(5));
1295
+ try expectLog1(&.{ N(ArgType.Transform), 0b1100, 0b0101 }, buf[0..3]);
1296
+ stream.reset();
1297
+ }