@pkmn/engine 0.1.0-dev.90d6c14c → 0.1.0-dev.9257876b

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 (48) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +52 -21
  3. package/build/pkg/addon.js.map +1 -1
  4. package/build/pkg/data/ids.json +12 -12
  5. package/build/pkg/data/layout.json +2 -2
  6. package/build/pkg/data/protocol.json +69 -12
  7. package/build/pkg/data.d.ts +3 -2
  8. package/build/pkg/data.js.map +1 -1
  9. package/build/pkg/gen1.js +2 -2
  10. package/build/pkg/gen1.js.map +1 -1
  11. package/build/pkg/index.d.ts +2 -1
  12. package/build/pkg/index.js.map +1 -1
  13. package/build/pkg/protocol.js +57 -38
  14. package/build/pkg/protocol.js.map +1 -1
  15. package/build/test/display.js.map +1 -1
  16. package/build/tools/debug.js +3 -1
  17. package/build/tools/debug.js.map +1 -1
  18. package/build.zig +125 -102
  19. package/package.json +43 -23
  20. package/src/bin/install-pkmn-engine +27 -19
  21. package/src/lib/common/data.zig +12 -24
  22. package/src/lib/common/optional.zig +9 -2
  23. package/src/lib/common/protocol.zig +805 -327
  24. package/src/lib/common/rational.zig +12 -8
  25. package/src/lib/common/rng.zig +26 -17
  26. package/src/lib/gen1/calc.zig +9 -10
  27. package/src/lib/gen1/chance.zig +83 -66
  28. package/src/lib/gen1/data/moves.zig +192 -180
  29. package/src/lib/gen1/data/species.zig +3 -3
  30. package/src/lib/gen1/data/types.zig +7 -7
  31. package/src/lib/gen1/data.zig +60 -67
  32. package/src/lib/gen1/helpers.zig +261 -123
  33. package/src/lib/gen1/mechanics.zig +370 -403
  34. package/src/lib/gen2/calc.zig +143 -0
  35. package/src/lib/gen2/chance.zig +1315 -0
  36. package/src/lib/gen2/data/items.zig +12 -18
  37. package/src/lib/gen2/data/moves.zig +663 -569
  38. package/src/lib/gen2/data/species.zig +1 -1
  39. package/src/lib/gen2/data/types.zig +134 -31
  40. package/src/lib/gen2/data.zig +106 -79
  41. package/src/lib/gen2/generated.zig +806 -0
  42. package/src/lib/gen2/helpers.zig +8 -12
  43. package/src/lib/gen2/mechanics.zig +3587 -9
  44. package/src/lib/{binding/node.zig → node.zig} +10 -13
  45. package/src/lib/pkmn.zig +26 -3
  46. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
  47. package/src/lib/gen1/README.md +0 -812
  48. package/src/lib/gen2/README.md +0 -308
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -1091,8 +1091,8 @@ pub const Species = enum(u8) {
1091
1091
  /// The number of Pokémon species in this generation.
1092
1092
  pub const size = 151;
1093
1093
 
1094
- /// The Pokémon's critical hit rate=io out of 256.
1095
- pub inline fn chance(id: Species) u8 {
1094
+ /// The Pokémon's critical hit ratio out of 256.
1095
+ pub fn chance(id: Species) u8 {
1096
1096
  assert(id != .None);
1097
1097
  return CHANCES[@intFromEnum(id) - 1];
1098
1098
  }
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
30
30
  Ice,
31
31
  Dragon,
32
32
 
33
- const CHART = [15][15]Effectiveness{
33
+ const CHART: [15][15]Effectiveness = .{
34
34
  [_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
35
35
  [_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
36
36
  [_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
@@ -90,16 +90,16 @@ pub const Type = enum(u4) {
90
90
  pub const size = 15;
91
91
 
92
92
  /// Whether or not this type is considered to be special as opposed to physical.
93
- pub inline fn special(self: Type) bool {
93
+ pub fn special(self: Type) bool {
94
94
  return @intFromEnum(self) >= @intFromEnum(Type.Fire);
95
95
  }
96
96
 
97
97
  /// The `Effectiveness` of type `t2` vs. type `t1`.
98
- pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
98
+ pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
99
99
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
100
  }
101
101
 
102
- /// The precedence order of type `t2` vs. type `t1`.
102
+ /// The precedence order of Type `t2` vs. Type `t1`.
103
103
  pub fn precedence(t1: Type, t2: Type) u8 {
104
104
  for (PRECEDENCE, 0..) |matchup, i| {
105
105
  if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
@@ -120,12 +120,12 @@ pub const Types = packed struct {
120
120
  }
121
121
 
122
122
  /// Whether this typing is immune to type `t`.
123
- pub inline fn immune(self: Types, t: Type) bool {
123
+ pub fn immune(self: Types, t: Type) bool {
124
124
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
125
125
  }
126
126
 
127
127
  /// Whether this typing includes type `t`.
128
- pub inline fn includes(self: Types, t: Type) bool {
128
+ pub fn includes(self: Types, t: Type) bool {
129
129
  return self.type1 == t or self.type2 == t;
130
130
  }
131
131
  };
@@ -65,27 +65,36 @@ pub fn Battle(comptime RNG: anytype) type {
65
65
  /// The last damage dealt by either side.
66
66
  last_damage: u16 = 0,
67
67
  /// Details about the last move selected/used by each player required to detect desyncs.
68
- last_moves: MoveDetails = .{},
68
+ last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
69
69
  /// The pseudo random number generator.
70
70
  rng: RNG,
71
71
 
72
72
  /// Returns the `Side` for the given `player`.
73
- pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
73
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
74
74
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
75
75
  return &self.sides[@intFromEnum(player)];
76
76
  }
77
77
 
78
78
  /// Returns the `Side` of the opponent for the given `player`
79
- pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
79
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
80
80
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
81
81
  return &self.sides[@intFromEnum(player.foe())];
82
82
  }
83
83
 
84
84
  /// Returns an identifier for the active Pokémon of `player`.
85
- pub inline fn active(self: *const Self, player: Player) ID {
85
+ pub fn active(self: *const Self, player: Player) ID {
86
86
  return player.ident(@intCast(self.side(player).order[0]));
87
87
  }
88
88
 
89
+ /// Returns the `MoveDetails` for the given `player`.
90
+ pub fn lastMove(
91
+ self: anytype,
92
+ player: Player,
93
+ ) PointerType(@TypeOf(self), MoveDetails) {
94
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
95
+ return &self.last_moves[@intFromEnum(player)];
96
+ }
97
+
89
98
  /// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
90
99
  /// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
91
100
  /// is enabled.
@@ -103,7 +112,7 @@ pub fn Battle(comptime RNG: anytype) type {
103
112
  /// make (i.e. on the cartridge a soft-lock occurs).
104
113
  ///
105
114
  /// This function will always return choices > 0 if `pkmn.options.showdown` is true.
106
- pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
115
+ pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
107
116
  return mechanics.choices(self, player, request, out);
108
117
  }
109
118
  };
@@ -132,7 +141,7 @@ pub const Side = extern struct {
132
141
  }
133
142
 
134
143
  /// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
135
- pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
144
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
145
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
137
146
  assert(slot > 0 and slot <= 6);
138
147
  const id = self.order[slot - 1];
@@ -141,7 +150,7 @@ pub const Side = extern struct {
141
150
  }
142
151
 
143
152
  /// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
144
- pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
153
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
154
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
146
155
  return self.get(1);
147
156
  }
@@ -167,7 +176,7 @@ pub const ActivePokemon = extern struct {
167
176
  }
168
177
 
169
178
  /// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
170
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
179
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
171
180
  assert(@typeInfo(@TypeOf(self)).Pointer.child == ActivePokemon);
172
181
  assert(mslot > 0 and mslot <= 4);
173
182
  assert(self.moves[mslot - 1].id != .None);
@@ -197,7 +206,7 @@ pub const Pokemon = extern struct {
197
206
  }
198
207
 
199
208
  /// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
200
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
209
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
201
210
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
202
211
  assert(mslot > 0 and mslot <= 4);
203
212
  assert(self.moves[mslot - 1].id != .None);
@@ -205,7 +214,7 @@ pub const Pokemon = extern struct {
205
214
  }
206
215
  };
207
216
 
208
- /// Representation of a Generation I & II Pokémon's move slot in a battle.
217
+ /// Representation of a Generation I Pokémon's move slot in a battle.
209
218
  pub const MoveSlot = extern struct {
210
219
  /// The identifier for the move.
211
220
  id: Move = .None,
@@ -213,24 +222,16 @@ pub const MoveSlot = extern struct {
213
222
  pp: u8 = 0,
214
223
 
215
224
  comptime {
216
- assert(@sizeOf(MoveSlot) == @sizeOf(u16));
225
+ assert(@sizeOf(MoveSlot) == 2);
217
226
  }
218
227
  };
219
228
 
220
229
  /// Details required to detect desyncs based on the move last selected/executed by players.
221
- pub const MoveDetails = packed struct {
222
- /// The move slot index of the last move selected in the battle menu by Player 1
223
- p1_index: if (showdown) u8 else u4 = 0,
224
- /// Whether the last move executed by Player 1 was counterable.
225
- p1_counterable: if (showdown) u8 else u4 = 0,
226
- /// The move slot index of the last move selected in the battle menu by Player 2
227
- p2_index: if (showdown) u8 else u4 = 0,
228
- /// Whether the last move executed by Player 2 was counterable.
229
- p2_counterable: if (showdown) u8 else u4 = 0,
230
-
231
- comptime {
232
- assert(@sizeOf(MoveDetails) == if (showdown) 4 else 2);
233
- }
230
+ pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
231
+ /// The move slot index of the last move selected in the battle menu.
232
+ index: if (showdown) u8 else u4 = 0,
233
+ /// Whether the last move executed was counterable.
234
+ counterable: if (showdown) u8 else u4 = 0,
234
235
  };
235
236
 
236
237
  /// Bitfield representation of a Generation I & II Pokémon's major status condition.
@@ -257,36 +258,36 @@ pub const Status = enum(u8) {
257
258
  const SLP = 0b111;
258
259
 
259
260
  /// Whether or not the status `num` is the same as `status`.
260
- pub inline fn is(num: u8, status: Status) bool {
261
+ pub fn is(num: u8, status: Status) bool {
261
262
  if (status == .SLP) return Status.duration(num) > 0;
262
263
  return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
263
264
  }
264
265
 
265
266
  /// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
266
- pub inline fn init(status: Status) u8 {
267
+ pub fn init(status: Status) u8 {
267
268
  assert(status != .SLP and status != .EXT);
268
269
  return @as(u8, 1) << @intCast(@intFromEnum(status));
269
270
  }
270
271
 
271
272
  /// Initializes a non self-inflicted sleep status with duration `dur`.
272
- pub inline fn slp(dur: u3) u8 {
273
+ pub fn slp(dur: u3) u8 {
273
274
  assert(dur > 0);
274
275
  return @as(u8, dur);
275
276
  }
276
277
 
277
278
  /// Initializes a self-inflicted sleep status with duration `dur`.
278
- pub inline fn slf(dur: u3) u8 {
279
+ pub fn slf(dur: u3) u8 {
279
280
  assert(dur > 0);
280
281
  return 0x80 | slp(dur);
281
282
  }
282
283
 
283
284
  /// Returns the duration of a sleep status.
284
- pub inline fn duration(num: u8) u3 {
285
+ pub fn duration(num: u8) u3 {
285
286
  return @intCast(num & SLP);
286
287
  }
287
288
 
288
289
  /// Returns whether `num` reflects any status.
289
- pub inline fn any(num: u8) bool {
290
+ pub fn any(num: u8) bool {
290
291
  return num > 0;
291
292
  }
292
293
 
@@ -315,16 +316,16 @@ test Status {
315
316
  try expect(Status.is(Status.slp(5), .SLP));
316
317
  try expect(!Status.is(Status.slp(5), .EXT));
317
318
  try expect(!Status.is(Status.slp(7), .PSN));
318
- try expectEqual(@as(u3, 5), Status.duration(Status.slp(5)));
319
+ try expectEqual(5, Status.duration(Status.slp(5)));
319
320
  try expect(Status.is(Status.slf(2), .SLP));
320
321
  try expect(Status.is(Status.slf(2), .EXT));
321
322
  try expect(Status.is(Status.TOX, .PSN));
322
323
  try expect(Status.is(Status.TOX, .EXT));
323
- try expectEqual(@as(u3, 1), Status.duration(Status.slp(1)));
324
+ try expectEqual(1, Status.duration(Status.slp(1)));
324
325
  }
325
326
 
326
327
  /// Bitfield representation of volatile statuses and associated data in Generation I.
327
- pub const Volatiles = packed struct {
328
+ pub const Volatiles = packed struct(u64) {
328
329
  /// Whether the "Bide" volatile status is present.
329
330
  Bide: bool = false,
330
331
  /// Whether the "Thrashing" volatile status is present.
@@ -377,19 +378,15 @@ pub const Volatiles = packed struct {
377
378
  /// The identity of whom the active Pokémon is transformed into.
378
379
  transform: u4 = 0,
379
380
  /// The remaining turns the move is disabled.
380
- disabled_duration: u4 = 0,
381
+ disable_duration: u4 = 0,
381
382
  /// The move slot (1-4) that is disabled.
382
- disabled_move: u3 = 0,
383
+ disable_move: u3 = 0,
383
384
  /// The number of turns toxic damage has been accumulating.
384
385
  toxic: u5 = 0,
385
-
386
- comptime {
387
- assert(@sizeOf(Volatiles) == 8);
388
- }
389
386
  };
390
387
 
391
388
  test Volatiles {
392
- var volatiles = Volatiles{};
389
+ var volatiles: Volatiles = .{};
393
390
  volatiles.Confusion = true;
394
391
  volatiles.confusion = 2;
395
392
  volatiles.Thrashing = true;
@@ -398,8 +395,8 @@ test Volatiles {
398
395
  volatiles.Substitute = true;
399
396
  volatiles.substitute = 42;
400
397
  volatiles.toxic = 4;
401
- volatiles.disabled_move = 2;
402
- volatiles.disabled_duration = 4;
398
+ volatiles.disable_move = 2;
399
+ volatiles.disable_duration = 4;
403
400
 
404
401
  try expect(volatiles.Confusion);
405
402
  try expect(volatiles.Thrashing);
@@ -408,13 +405,13 @@ test Volatiles {
408
405
  try expect(!volatiles.Transform);
409
406
  try expect(!volatiles.MultiHit);
410
407
 
411
- try expectEqual(@as(u16, 235), volatiles.state);
412
- try expectEqual(@as(u8, 42), volatiles.substitute);
413
- try expectEqual(@as(u4, 2), volatiles.disabled_move);
414
- try expectEqual(@as(u4, 4), volatiles.disabled_duration);
415
- try expectEqual(@as(u4, 2), volatiles.confusion);
416
- try expectEqual(@as(u5, 4), volatiles.toxic);
417
- try expectEqual(@as(u4, 3), volatiles.attacks);
408
+ try expectEqual(235, volatiles.state);
409
+ try expectEqual(42, volatiles.substitute);
410
+ try expectEqual(2, volatiles.disable_move);
411
+ try expectEqual(4, volatiles.disable_duration);
412
+ try expectEqual(2, volatiles.confusion);
413
+ try expectEqual(4, volatiles.toxic);
414
+ try expectEqual(3, volatiles.attacks);
418
415
  }
419
416
 
420
417
  /// Representaiton of a Pokémon's stats in Generation I.
@@ -445,13 +442,13 @@ test Stats {
445
442
  try expectEqual(0, stats.def);
446
443
 
447
444
  var base = Species.get(.Gyarados).stats;
448
- try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
445
+ try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
449
446
  base = Species.get(.Pidgeot).stats;
450
- try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
447
+ try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
451
448
  }
452
449
 
453
450
  /// Representation of a Pokémon's boosts in Generation I.
454
- pub const Boosts = packed struct {
451
+ pub const Boosts = packed struct(u32) {
455
452
  /// A Pokémon's Attack boosts.
456
453
  atk: i4 = 0,
457
454
  /// A Pokémon's Defense boosts.
@@ -466,14 +463,10 @@ pub const Boosts = packed struct {
466
463
  evasion: i4 = 0,
467
464
 
468
465
  _: u8 = 0,
469
-
470
- comptime {
471
- assert(@sizeOf(Boosts) == 4);
472
- }
473
466
  };
474
467
 
475
468
  test Boosts {
476
- const boosts = Boosts{ .spc = -6 };
469
+ const boosts: Boosts = .{ .spc = -6 };
477
470
  try expectEqual(0, boosts.atk);
478
471
  try expectEqual(-6, boosts.spc);
479
472
  }
@@ -485,7 +478,7 @@ test Move {
485
478
  try expectEqual(2, @intFromEnum(Move.KarateChop));
486
479
  const move = Move.get(.Fissure);
487
480
  try expectEqual(Move.Effect.OHKO, move.effect);
488
- try expectEqual(@as(u8, 30), move.accuracy);
481
+ try expectEqual(rng.Gen12.percent(30), move.accuracy);
489
482
  try expectEqual(Type.Ground, move.type);
490
483
 
491
484
  try expect(!Move.Effect.onBegin(.None));
@@ -544,7 +537,7 @@ pub const Species = species.Species;
544
537
 
545
538
  test Species {
546
539
  try expectEqual(2, @intFromEnum(Species.Ivysaur));
547
- try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
540
+ try expectEqual(100, Species.get(.Mew).stats.def);
548
541
  }
549
542
 
550
543
  /// Representation of a Generation I type in Pokémon.
@@ -583,8 +576,8 @@ test Types {
583
576
  try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
584
577
  try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
585
578
 
586
- try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
587
- try expectEqual(@as(u8, 26), Type.precedence(.Bug, .Ghost));
579
+ try expectEqual(3, Type.precedence(.Water, .Water));
580
+ try expectEqual(26, Type.precedence(.Bug, .Ghost));
588
581
  try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
589
582
 
590
583
  const t: Types = .{ .type1 = .Rock, .type2 = .Ground };
@@ -623,19 +616,19 @@ pub const DVs = struct {
623
616
  };
624
617
 
625
618
  test DVs {
626
- var dvs = DVs{ .spc = 15, .spe = 15 };
627
- try expectEqual(@as(u4, 15), dvs.hp());
628
- dvs = DVs{
619
+ var dvs: DVs = .{ .spc = 15, .spe = 15 };
620
+ try expectEqual(15, dvs.hp());
621
+ dvs = .{
629
622
  .atk = 5,
630
623
  .def = 15,
631
624
  .spe = 13,
632
625
  .spc = 13,
633
626
  };
634
- try expectEqual(@as(u4, 15), dvs.hp());
635
- dvs = DVs{
627
+ try expectEqual(15, dvs.hp());
628
+ dvs = .{
636
629
  .def = 3,
637
630
  .spe = 10,
638
631
  .spc = 11,
639
632
  };
640
- try expectEqual(@as(u4, 13), dvs.hp());
633
+ try expectEqual(13, dvs.hp());
641
634
  }