@pkmn/engine 0.1.0-dev.5ea92492 → 0.1.0-dev.5fbc7aef

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