@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d73cd76c

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 (87) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +114 -69
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +26 -4
  13. package/build/pkg/addon.js +39 -70
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/ids.json +12 -12
  16. package/build/pkg/data/layout.json +39 -4
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +15 -18
  21. package/build/pkg/gen1.js +43 -37
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +42 -91
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +29 -1
  27. package/build/pkg/protocol.js +123 -102
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +84 -73
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +66 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +360 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +157 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +428 -176
  40. package/package.json +72 -122
  41. package/src/bin/install-pkmn-engine +56 -25
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +201 -0
  44. package/src/lib/bindings/node.zig +117 -0
  45. package/src/lib/bindings/wasm.zig +51 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/battle.zig +17 -0
  48. package/src/lib/common/data.zig +19 -31
  49. package/src/lib/common/debug.zig +58 -33
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +71 -0
  52. package/src/lib/common/options.zig +15 -0
  53. package/src/lib/common/protocol.zig +1037 -462
  54. package/src/lib/common/rational.zig +440 -0
  55. package/src/lib/common/rng.zig +31 -24
  56. package/src/lib/common/util.zig +64 -0
  57. package/src/lib/gen1/calc.zig +975 -0
  58. package/src/lib/gen1/chance.zig +1246 -0
  59. package/src/lib/gen1/data/moves.zig +203 -193
  60. package/src/lib/gen1/data/species.zig +6 -8
  61. package/src/lib/gen1/data/types.zig +12 -13
  62. package/src/lib/gen1/data.zig +98 -89
  63. package/src/lib/gen1/helpers.zig +159 -51
  64. package/src/lib/gen1/mechanics.zig +1211 -846
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +914 -0
  67. package/src/lib/gen2/data/items.zig +15 -23
  68. package/src/lib/gen2/data/moves.zig +665 -573
  69. package/src/lib/gen2/data/species.zig +3 -5
  70. package/src/lib/gen2/data/types.zig +141 -36
  71. package/src/lib/gen2/data.zig +136 -100
  72. package/src/lib/gen2/generated.zig +801 -0
  73. package/src/lib/gen2/helpers.zig +81 -58
  74. package/src/lib/gen2/mechanics.zig +3582 -30
  75. package/src/lib/napi.h +2 -0
  76. package/src/lib/node.zig +14 -0
  77. package/src/lib/pkmn.zig +172 -17
  78. package/src/lib/wasm.zig +13 -0
  79. package/src/tools/display/dom.ts +108 -0
  80. package/src/tools/display/ui.css +354 -0
  81. package/build/test/display.js +0 -400
  82. package/build/test/display.js.map +0 -1
  83. package/src/lib/binding/node.zig +0 -229
  84. package/src/lib/binding/wasm.zig +0 -10
  85. package/src/lib/gen1/README.md +0 -812
  86. package/src/lib/gen2/README.md +0 -309
  87. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,11 +1,9 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
2
-
3
- const std = @import("std");
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
4
2
 
5
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
-
9
7
  const Stats = gen1.Stats;
10
8
  const Types = gen1.Types;
11
9
 
@@ -1091,15 +1089,15 @@ pub const Species = enum(u8) {
1091
1089
  /// The number of Pokémon species in this generation.
1092
1090
  pub const size = 151;
1093
1091
 
1094
- /// The Pokémon's critical hit rate=io out of 256.
1095
- pub inline fn chance(id: Species) u8 {
1092
+ /// The Pokémon's critical hit ratio out of 256.
1093
+ pub fn chance(id: Species) u8 {
1096
1094
  assert(id != .None);
1097
- return CHANCES[@enumToInt(id) - 1];
1095
+ return CHANCES[@intFromEnum(id) - 1];
1098
1096
  }
1099
1097
 
1100
1098
  /// Returns the `Data` corresponding to the species.
1101
1099
  pub fn get(id: Species) Data {
1102
1100
  assert(id != .None);
1103
- return DATA[@enumToInt(id) - 1];
1101
+ return DATA[@intFromEnum(id) - 1];
1104
1102
  }
1105
1103
  };
@@ -1,8 +1,7 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
2
-
3
- const std = @import("std");
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
4
2
 
5
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
7
  const Effectiveness = gen1.Effectiveness;
@@ -30,7 +29,7 @@ pub const Type = enum(u4) {
30
29
  Ice,
31
30
  Dragon,
32
31
 
33
- const CHART = [15][15]Effectiveness{
32
+ const CHART: [15][15]Effectiveness = .{
34
33
  [_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
35
34
  [_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
36
35
  [_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
@@ -90,26 +89,26 @@ pub const Type = enum(u4) {
90
89
  pub const size = 15;
91
90
 
92
91
  /// Whether or not this type is considered to be special as opposed to physical.
93
- pub inline fn special(self: Type) bool {
94
- return @enumToInt(self) >= @enumToInt(Type.Fire);
92
+ pub fn special(self: Type) bool {
93
+ return @intFromEnum(self) >= @intFromEnum(Type.Fire);
95
94
  }
96
95
 
97
96
  /// The `Effectiveness` of type `t2` vs. type `t1`.
98
- pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
99
- return CHART[@enumToInt(t1)][@enumToInt(t2)];
97
+ pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
98
+ return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
99
  }
101
100
 
102
- /// The precedence order of type `t2` vs. type `t1`.
101
+ /// The precedence order of Type `t1` vs. Type `t2`.
103
102
  pub fn precedence(t1: Type, t2: Type) u8 {
104
103
  for (PRECEDENCE, 0..) |matchup, i| {
105
- if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(u8, i);
104
+ if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
106
105
  }
107
106
  unreachable;
108
107
  }
109
108
  };
110
109
 
111
110
  /// Representation of a Pokémon's typing.
112
- pub const Types = packed struct {
111
+ pub const Types = packed struct(u8) {
113
112
  /// A Pokémon's primary type.
114
113
  type1: Type = .Normal,
115
114
  /// A Pokémon's secondary type (may be identical to its primary type).
@@ -120,12 +119,12 @@ pub const Types = packed struct {
120
119
  }
121
120
 
122
121
  /// Whether this typing is immune to type `t`.
123
- pub inline fn immune(self: Types, t: Type) bool {
122
+ pub fn immune(self: Types, t: Type) bool {
124
123
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
125
124
  }
126
125
 
127
126
  /// Whether this typing includes type `t`.
128
- pub inline fn includes(self: Types, t: Type) bool {
127
+ pub fn includes(self: Types, t: Type) bool {
129
128
  return self.type1 == t or self.type2 == t;
130
129
  }
131
130
  };
@@ -1,16 +1,16 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
2
+ const calc = @import("calc.zig");
3
+ const chance = @import("chance.zig");
4
4
  const data = @import("../common/data.zig");
5
- const options = @import("../common/options.zig");
5
+ const mechanics = @import("mechanics.zig");
6
+ const moves = @import("data/moves.zig");
7
+ const pkmn = @import("../pkmn.zig");
6
8
  const protocol = @import("../common/protocol.zig");
7
9
  const rng = @import("../common/rng.zig");
8
-
9
- const moves = @import("data/moves.zig");
10
10
  const species = @import("data/species.zig");
11
+ const std = @import("std");
11
12
  const types = @import("data/types.zig");
12
-
13
- const mechanics = @import("mechanics.zig");
13
+ const util = @import("../common/util.zig");
14
14
 
15
15
  const assert = std.debug.assert;
16
16
  const expectEqual = std.testing.expectEqual;
@@ -18,11 +18,11 @@ const expect = std.testing.expect;
18
18
 
19
19
  /// The minimum size in bytes required to hold all Generation I choice options.
20
20
  pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
21
- /// The optimal size in bytes required to hold all Generation I choice options.
22
- /// At least as large as MAX_CHOICES.
21
+ /// The maximum number of bytes possibly logged by a single Generation I update.
23
22
  pub const MAX_LOGS: usize = 180;
24
23
 
25
- /// The maximum number of bytes possibly logged by a single Generation I update.
24
+ /// The optimal size in bytes required to hold all Generation I choice options.
25
+ /// At least as large as MAX_CHOICES.
26
26
  pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
27
27
  MAX_CHOICES
28
28
  else
@@ -34,12 +34,18 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
34
34
  else
35
35
  std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
36
36
 
37
+ /// Null object pattern implementation of pkmn.battle.Options for Generation I.
38
+ pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
39
+
37
40
  const Choice = data.Choice;
38
41
  const ID = data.ID;
39
42
  const Player = data.Player;
40
43
  const Result = data.Result;
41
44
 
42
- const showdown = options.showdown;
45
+ const PointerType = util.PointerType;
46
+ const isPointerTo = util.isPointerTo;
47
+
48
+ const showdown = pkmn.options.showdown;
43
49
 
44
50
  /// The pseudo random number generator used by Generation I.
45
51
  pub const PRNG = rng.PRNG(1);
@@ -56,29 +62,41 @@ pub fn Battle(comptime RNG: anytype) type {
56
62
  /// The last damage dealt by either side.
57
63
  last_damage: u16 = 0,
58
64
  /// Details about the last move selected/used by each player required to detect desyncs.
59
- last_moves: MoveDetails = .{},
65
+ last_moves: [2]MoveDetails = @splat(.{}),
60
66
  /// The pseudo random number generator.
61
67
  rng: RNG,
62
68
 
63
69
  /// Returns the `Side` for the given `player`.
64
- pub inline fn side(self: *Self, player: Player) *Side {
65
- return &self.sides[@enumToInt(player)];
70
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
+ assert(isPointerTo(self, Self));
72
+ return &self.sides[@intFromEnum(player)];
66
73
  }
67
74
 
68
75
  /// Returns the `Side` of the opponent for the given `player`
69
- pub inline fn foe(self: *Self, player: Player) *Side {
70
- return &self.sides[@enumToInt(player.foe())];
76
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
77
+ assert(isPointerTo(self, Self));
78
+ return &self.sides[@intFromEnum(player.foe())];
71
79
  }
72
80
 
73
81
  /// Returns an identifier for the active Pokémon of `player`.
74
- pub inline fn active(self: *Self, player: Player) ID {
75
- return player.ident(@intCast(u3, self.side(player).order[0]));
82
+ pub fn active(self: *const Self, player: Player) ID {
83
+ return player.ident(@intCast(self.side(player).order[0]));
84
+ }
85
+
86
+ /// Returns the `MoveDetails` for the given `player`.
87
+ pub fn lastMove(
88
+ self: anytype,
89
+ player: Player,
90
+ ) PointerType(@TypeOf(self), MoveDetails) {
91
+ assert(isPointerTo(self, Self));
92
+ return &self.last_moves[@intFromEnum(player)];
76
93
  }
77
94
 
78
95
  /// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
79
- /// battle, optionally writing protocol message logs to `log` if `options.log` is enabled.
80
- pub fn update(self: *Self, c1: Choice, c2: Choice, log: anytype) !Result {
81
- return mechanics.update(self, c1, c2, log);
96
+ /// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
97
+ /// is enabled.
98
+ pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
99
+ return mechanics.update(self, c1, c2, options);
82
100
  }
83
101
 
84
102
  /// Fills in at most `out.len` possible choices for the `player` given the previous `result`
@@ -90,8 +108,8 @@ pub fn Battle(comptime RNG: anytype) type {
90
108
  /// interacts with Disable, in which case there are no possible choices for the player to
91
109
  /// make (i.e. on the cartridge a soft-lock occurs).
92
110
  ///
93
- /// This function will always return a number of choices > 0 if options.showdown is true.
94
- pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
111
+ /// This function will always return choices > 0 if `pkmn.options.showdown` is true.
112
+ pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
95
113
  return mechanics.choices(self, player, request, out);
96
114
  }
97
115
  };
@@ -104,12 +122,12 @@ test Battle {
104
122
  /// Representation of one side of a Generation I Pokémon battle.
105
123
  pub const Side = extern struct {
106
124
  /// The player's party in its original order
107
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
125
+ pokemon: [6]Pokemon = @splat(.{}),
108
126
  /// The active Pokémon for the side, zero initialized if the battle has yet to start.
109
127
  /// Note that fainted Pokémon are still consider "active" until their replacement switches in.
110
128
  active: ActivePokemon = .{},
111
129
  /// One-based slot indexes reflecting the current order of the player's party.
112
- order: [6]u8 = [_]u8{0} ** 6,
130
+ order: [6]u8 = @splat(0),
113
131
  /// The last move the player selected.
114
132
  last_selected_move: Move = .None,
115
133
  /// The last move the player used.
@@ -120,7 +138,8 @@ pub const Side = extern struct {
120
138
  }
121
139
 
122
140
  /// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
123
- pub inline fn get(self: *Side, slot: u8) *Pokemon {
141
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
142
+ assert(isPointerTo(self, Side));
124
143
  assert(slot > 0 and slot <= 6);
125
144
  const id = self.order[slot - 1];
126
145
  assert(id > 0 and id <= 6);
@@ -128,7 +147,8 @@ pub const Side = extern struct {
128
147
  }
129
148
 
130
149
  /// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
131
- pub inline fn stored(self: *Side) *Pokemon {
150
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
151
+ assert(isPointerTo(self, Side));
132
152
  return self.get(1);
133
153
  }
134
154
  };
@@ -146,14 +166,15 @@ pub const ActivePokemon = extern struct {
146
166
  /// The active Pokémon's volatile statuses and associated data.
147
167
  volatiles: Volatiles = .{},
148
168
  /// The active Pokémon's current move slots.
149
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
169
+ moves: [4]MoveSlot = @splat(.{}),
150
170
 
151
171
  comptime {
152
172
  assert(@sizeOf(ActivePokemon) == 32);
153
173
  }
154
174
 
155
175
  /// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
156
- pub inline fn move(self: *ActivePokemon, mslot: u8) *MoveSlot {
176
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
177
+ assert(isPointerTo(self, ActivePokemon));
157
178
  assert(mslot > 0 and mslot <= 4);
158
179
  assert(self.moves[mslot - 1].id != .None);
159
180
  return &self.moves[mslot - 1];
@@ -165,7 +186,7 @@ pub const Pokemon = extern struct {
165
186
  /// The Pokémon's unmodified stats.
166
187
  stats: Stats(u16) = .{},
167
188
  /// The Pokémon's original stored move slots.
168
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
189
+ moves: [4]MoveSlot = @splat(.{}),
169
190
  /// The Pokémon's current HP.
170
191
  hp: u16 = 0,
171
192
  /// The Pokémon's current status.
@@ -182,14 +203,15 @@ pub const Pokemon = extern struct {
182
203
  }
183
204
 
184
205
  /// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
185
- pub inline fn move(self: *Pokemon, mslot: u8) *MoveSlot {
206
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
207
+ assert(isPointerTo(self, Pokemon));
186
208
  assert(mslot > 0 and mslot <= 4);
187
209
  assert(self.moves[mslot - 1].id != .None);
188
210
  return &self.moves[mslot - 1];
189
211
  }
190
212
  };
191
213
 
192
- /// Representation of a Generation I & II Pokémon's move slot in a battle.
214
+ /// Representation of a Generation I Pokémon's move slot in a battle.
193
215
  pub const MoveSlot = extern struct {
194
216
  /// The identifier for the move.
195
217
  id: Move = .None,
@@ -197,24 +219,16 @@ pub const MoveSlot = extern struct {
197
219
  pp: u8 = 0,
198
220
 
199
221
  comptime {
200
- assert(@sizeOf(MoveSlot) == @sizeOf(u16));
222
+ assert(@sizeOf(MoveSlot) == 2);
201
223
  }
202
224
  };
203
225
 
204
226
  /// Details required to detect desyncs based on the move last selected/executed by players.
205
- pub const MoveDetails = packed struct {
206
- /// The move slot index of the last move selected in the battle menu by Player 1
207
- p1_index: if (showdown) u8 else u3 = 0,
208
- /// Whether the last move executed by Player 1 was counterable.
209
- p1_counterable: if (showdown) u8 else u1 = 0,
210
- /// The move slot index of the last move selected in the battle menu by Player 2
211
- p2_index: if (showdown) u8 else u3 = 0,
212
- /// Whether the last move executed by Player 2 was counterable.
213
- p2_counterable: if (showdown) u8 else u1 = 0,
214
-
215
- comptime {
216
- assert(@sizeOf(MoveDetails) == if (showdown) 4 else 1);
217
- }
227
+ pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
228
+ /// The move slot index of the last move selected in the battle menu.
229
+ index: if (showdown) u8 else u4 = 0,
230
+ /// Whether the last move executed was counterable.
231
+ counterable: if (showdown) u8 else u4 = 0,
218
232
  };
219
233
 
220
234
  /// Bitfield representation of a Generation I & II Pokémon's major status condition.
@@ -238,39 +252,39 @@ pub const Status = enum(u8) {
238
252
 
239
253
  pub const TOX = 0b10001000;
240
254
 
241
- const SLP = 0b111;
255
+ const SLEEP = 0b111;
242
256
 
243
257
  /// Whether or not the status `num` is the same as `status`.
244
- pub inline fn is(num: u8, status: Status) bool {
258
+ pub fn is(num: u8, status: Status) bool {
245
259
  if (status == .SLP) return Status.duration(num) > 0;
246
- return ((num >> @intCast(u3, @enumToInt(status))) & 1) != 0;
260
+ return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
247
261
  }
248
262
 
249
263
  /// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
250
- pub inline fn init(status: Status) u8 {
264
+ pub fn init(status: Status) u8 {
251
265
  assert(status != .SLP and status != .EXT);
252
- return @as(u8, 1) << @intCast(u3, @enumToInt(status));
266
+ return @as(u8, 1) << @intCast(@intFromEnum(status));
253
267
  }
254
268
 
255
269
  /// Initializes a non self-inflicted sleep status with duration `dur`.
256
- pub inline fn slp(dur: u3) u8 {
270
+ pub fn slp(dur: u3) u8 {
257
271
  assert(dur > 0);
258
272
  return @as(u8, dur);
259
273
  }
260
274
 
261
275
  /// Initializes a self-inflicted sleep status with duration `dur`.
262
- pub inline fn slf(dur: u3) u8 {
276
+ pub fn slf(dur: u3) u8 {
263
277
  assert(dur > 0);
264
278
  return 0x80 | slp(dur);
265
279
  }
266
280
 
267
281
  /// Returns the duration of a sleep status.
268
- pub inline fn duration(num: u8) u3 {
269
- return @intCast(u3, num & SLP);
282
+ pub fn duration(num: u8) u3 {
283
+ return @intCast(num & SLEEP);
270
284
  }
271
285
 
272
286
  /// Returns whether `num` reflects any status.
273
- pub inline fn any(num: u8) bool {
287
+ pub fn any(num: u8) bool {
274
288
  return num > 0;
275
289
  }
276
290
 
@@ -308,7 +322,7 @@ test Status {
308
322
  }
309
323
 
310
324
  /// Bitfield representation of volatile statuses and associated data in Generation I.
311
- pub const Volatiles = packed struct {
325
+ pub const Volatiles = packed struct(u64) {
312
326
  /// Whether the "Bide" volatile status is present.
313
327
  Bide: bool = false,
314
328
  /// Whether the "Thrashing" volatile status is present.
@@ -361,19 +375,15 @@ pub const Volatiles = packed struct {
361
375
  /// The identity of whom the active Pokémon is transformed into.
362
376
  transform: u4 = 0,
363
377
  /// The remaining turns the move is disabled.
364
- disabled_duration: u4 = 0,
378
+ disable_duration: u4 = 0,
365
379
  /// The move slot (1-4) that is disabled.
366
- disabled_move: u3 = 0,
380
+ disable_move: u3 = 0,
367
381
  /// The number of turns toxic damage has been accumulating.
368
382
  toxic: u5 = 0,
369
-
370
- comptime {
371
- assert(@sizeOf(Volatiles) == 8);
372
- }
373
383
  };
374
384
 
375
385
  test Volatiles {
376
- var volatiles = Volatiles{};
386
+ var volatiles: Volatiles = .{};
377
387
  volatiles.Confusion = true;
378
388
  volatiles.confusion = 2;
379
389
  volatiles.Thrashing = true;
@@ -382,8 +392,8 @@ test Volatiles {
382
392
  volatiles.Substitute = true;
383
393
  volatiles.substitute = 42;
384
394
  volatiles.toxic = 4;
385
- volatiles.disabled_move = 2;
386
- volatiles.disabled_duration = 4;
395
+ volatiles.disable_move = 2;
396
+ volatiles.disable_duration = 4;
387
397
 
388
398
  try expect(volatiles.Confusion);
389
399
  try expect(volatiles.Thrashing);
@@ -394,14 +404,14 @@ test Volatiles {
394
404
 
395
405
  try expectEqual(@as(u16, 235), volatiles.state);
396
406
  try expectEqual(@as(u8, 42), volatiles.substitute);
397
- try expectEqual(@as(u4, 2), volatiles.disabled_move);
398
- try expectEqual(@as(u4, 4), volatiles.disabled_duration);
399
- try expectEqual(@as(u4, 2), volatiles.confusion);
407
+ try expectEqual(@as(u4, 2), volatiles.disable_move);
408
+ try expectEqual(@as(u4, 4), volatiles.disable_duration);
409
+ try expectEqual(@as(u3, 2), volatiles.confusion);
400
410
  try expectEqual(@as(u5, 4), volatiles.toxic);
401
- try expectEqual(@as(u4, 3), volatiles.attacks);
411
+ try expectEqual(@as(u3, 3), volatiles.attacks);
402
412
  }
403
413
 
404
- /// Representaiton of a Pokémon's stats in Generation I.
414
+ /// Representation of a Pokémon's stats in Generation I.
405
415
  pub fn Stats(comptime T: type) type {
406
416
  return extern struct {
407
417
  hp: T = 0,
@@ -413,10 +423,11 @@ pub fn Stats(comptime T: type) type {
413
423
  /// Computes the value of `stat` given a `base`, `dv`, stat `exp` and `level`.
414
424
  pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
415
425
  assert(level > 0 and level <= 100);
416
- const evs = @min(255, @floatToInt(u16, @ceil(@sqrt(@intToFloat(f32, exp)))));
426
+ const float: f32 = @floatFromInt(exp);
427
+ const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
417
428
  const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
418
429
  const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
419
- return @intCast(T, core *% @as(u32, level) / 100 +% factor);
430
+ return @intCast(core *% @as(u32, level) / 100 +% factor);
420
431
  }
421
432
  };
422
433
  }
@@ -434,7 +445,7 @@ test Stats {
434
445
  }
435
446
 
436
447
  /// Representation of a Pokémon's boosts in Generation I.
437
- pub const Boosts = packed struct {
448
+ pub const Boosts = packed struct(u32) {
438
449
  /// A Pokémon's Attack boosts.
439
450
  atk: i4 = 0,
440
451
  /// A Pokémon's Defense boosts.
@@ -449,14 +460,10 @@ pub const Boosts = packed struct {
449
460
  evasion: i4 = 0,
450
461
 
451
462
  _: u8 = 0,
452
-
453
- comptime {
454
- assert(@sizeOf(Boosts) == 4);
455
- }
456
463
  };
457
464
 
458
465
  test Boosts {
459
- const boosts = Boosts{ .spc = -6 };
466
+ const boosts: Boosts = .{ .spc = -6 };
460
467
  try expectEqual(0, boosts.atk);
461
468
  try expectEqual(-6, boosts.spc);
462
469
  }
@@ -465,10 +472,10 @@ test Boosts {
465
472
  pub const Move = moves.Move;
466
473
 
467
474
  test Move {
468
- try expectEqual(2, @enumToInt(Move.KarateChop));
475
+ try expectEqual(2, @intFromEnum(Move.KarateChop));
469
476
  const move = Move.get(.Fissure);
470
477
  try expectEqual(Move.Effect.OHKO, move.effect);
471
- try expectEqual(@as(u8, 30), move.accuracy);
478
+ try expectEqual(rng.Gen12.percent(30), move.accuracy);
472
479
  try expectEqual(Type.Ground, move.type);
473
480
 
474
481
  try expect(!Move.Effect.onBegin(.None));
@@ -526,7 +533,7 @@ test Move {
526
533
  pub const Species = species.Species;
527
534
 
528
535
  test Species {
529
- try expectEqual(2, @enumToInt(Species.Ivysaur));
536
+ try expectEqual(2, @intFromEnum(Species.Ivysaur));
530
537
  try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
531
538
  }
532
539
 
@@ -543,9 +550,11 @@ pub const Effectiveness = enum(u8) {
543
550
  Super = 20,
544
551
 
545
552
  /// Used to determine the effectiness of a neutral damage hit.
546
- pub const neutral: u16 = @enumToInt(Effectiveness.Neutral) * @enumToInt(Effectiveness.Neutral);
553
+ pub const neutral: u16 =
554
+ @intFromEnum(Effectiveness.Neutral) * @intFromEnum(Effectiveness.Neutral);
547
555
  /// Used to detect whether there is a mismatch in type effectivness (relevant for precedence).
548
- pub const mismatch: u16 = @enumToInt(Effectiveness.Resisted) + @enumToInt(Effectiveness.Super);
556
+ pub const mismatch: u16 =
557
+ @intFromEnum(Effectiveness.Resisted) + @intFromEnum(Effectiveness.Super);
549
558
 
550
559
  comptime {
551
560
  assert(@sizeOf(Effectiveness) == 1);
@@ -553,8 +562,8 @@ pub const Effectiveness = enum(u8) {
553
562
  };
554
563
 
555
564
  test Types {
556
- try expectEqual(14, @enumToInt(Type.Dragon));
557
- try expectEqual(20, @enumToInt(Effectiveness.Super));
565
+ try expectEqual(14, @intFromEnum(Type.Dragon));
566
+ try expectEqual(20, @intFromEnum(Effectiveness.Super));
558
567
 
559
568
  try expect(!Type.Ghost.special());
560
569
  try expect(Type.Dragon.special());
@@ -604,16 +613,16 @@ pub const DVs = struct {
604
613
  };
605
614
 
606
615
  test DVs {
607
- var dvs = DVs{ .spc = 15, .spe = 15 };
616
+ var dvs: DVs = .{ .spc = 15, .spe = 15 };
608
617
  try expectEqual(@as(u4, 15), dvs.hp());
609
- dvs = DVs{
618
+ dvs = .{
610
619
  .atk = 5,
611
620
  .def = 15,
612
621
  .spe = 13,
613
622
  .spc = 13,
614
623
  };
615
624
  try expectEqual(@as(u4, 15), dvs.hp());
616
- dvs = DVs{
625
+ dvs = .{
617
626
  .def = 3,
618
627
  .spe = 10,
619
628
  .spc = 11,