@pkmn/engine 0.1.0-dev.dd5b9081 → 0.1.0-dev.e0e37c1b

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 (38) hide show
  1. package/README.md +5 -5
  2. package/build/pkg/data/ids.json +12 -12
  3. package/build/pkg/data/layout.json +2 -2
  4. package/build/pkg/data/protocol.json +69 -12
  5. package/build/pkg/data.d.ts +2 -2
  6. package/build/pkg/gen1.js +2 -2
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/protocol.js +56 -37
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build/tools/debug.js +3 -1
  11. package/build/tools/debug.js.map +1 -1
  12. package/build.zig +21 -15
  13. package/package.json +28 -16
  14. package/src/lib/binding/node.zig +1 -1
  15. package/src/lib/common/data.zig +5 -5
  16. package/src/lib/common/optional.zig +8 -1
  17. package/src/lib/common/protocol.zig +695 -270
  18. package/src/lib/common/rational.zig +5 -5
  19. package/src/lib/common/rng.zig +14 -5
  20. package/src/lib/gen1/README.md +28 -28
  21. package/src/lib/gen1/calc.zig +4 -4
  22. package/src/lib/gen1/chance.zig +53 -29
  23. package/src/lib/gen1/data/moves.zig +190 -176
  24. package/src/lib/gen1/data/species.zig +2 -2
  25. package/src/lib/gen1/data/types.zig +7 -7
  26. package/src/lib/gen1/data.zig +25 -25
  27. package/src/lib/gen1/helpers.zig +26 -41
  28. package/src/lib/gen1/mechanics.zig +283 -324
  29. package/src/lib/gen2/README.md +18 -72
  30. package/src/lib/gen2/calc.zig +143 -0
  31. package/src/lib/gen2/chance.zig +1314 -0
  32. package/src/lib/gen2/data/items.zig +12 -18
  33. package/src/lib/gen2/data/moves.zig +377 -344
  34. package/src/lib/gen2/data/species.zig +1 -1
  35. package/src/lib/gen2/data/types.zig +134 -31
  36. package/src/lib/gen2/data.zig +44 -36
  37. package/src/lib/gen2/generated.zig +808 -0
  38. package/src/lib/gen2/mechanics.zig +3000 -206
@@ -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
 
@@ -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
 
@@ -14,15 +14,15 @@ const I = Effectiveness.Immune;
14
14
 
15
15
  /// Representation of a Generation II type in Pokémon.
16
16
  pub const Type = enum(u8) {
17
+ Ground,
18
+ Rock,
19
+ Steel,
17
20
  Normal,
18
21
  Fighting,
19
22
  Flying,
20
23
  Poison,
21
- Ground,
22
- Rock,
23
24
  Bug,
24
25
  Ghost,
25
- Steel,
26
26
  @"???",
27
27
  Fire,
28
28
  Water,
@@ -33,37 +33,37 @@ pub const Type = enum(u8) {
33
33
  Dragon,
34
34
  Dark,
35
35
 
36
- const CHART = [18][18]Effectiveness{
37
- [_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
38
- [_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
39
- [_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
40
- [_]Effectiveness{ N, N, N, R, R, R, N, R, I, N, N, N, S, N, N, N, N, N }, // Poison
41
- [_]Effectiveness{ N, N, I, S, N, S, R, N, S, N, S, N, R, S, N, N, N, N }, // Ground
42
- [_]Effectiveness{ N, R, S, N, R, N, S, N, R, N, S, N, N, N, N, S, N, N }, // Rock
43
- [_]Effectiveness{ N, R, R, R, N, N, N, R, R, N, R, N, S, N, S, N, N, S }, // Bug
44
- [_]Effectiveness{ I, N, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Ghost
45
- [_]Effectiveness{ N, N, N, N, N, S, N, N, R, N, R, R, N, R, N, S, N, N }, // Steel
36
+ const CHART: [18][18]Effectiveness = .{
37
+ [_]Effectiveness{ N, S, S, N, N, I, S, R, N, N, S, N, R, S, N, N, N, N }, // Ground
38
+ [_]Effectiveness{ R, N, R, N, R, S, N, S, N, N, S, N, N, N, N, S, N, N }, // Rock
39
+ [_]Effectiveness{ N, S, R, N, N, N, N, N, N, N, R, R, N, R, N, S, N, N }, // Steel
40
+ [_]Effectiveness{ N, R, R, N, N, N, N, N, I, N, N, N, N, N, N, N, N, N }, // Normal
41
+ [_]Effectiveness{ N, S, S, S, N, R, R, R, I, N, N, N, N, N, R, S, N, S }, // Fighting
42
+ [_]Effectiveness{ N, R, R, N, S, N, N, S, N, N, N, N, S, R, N, N, N, N }, // Flying
43
+ [_]Effectiveness{ R, R, I, N, N, N, R, N, R, N, N, N, S, N, N, N, N, N }, // Poison
44
+ [_]Effectiveness{ N, N, R, N, R, R, R, N, R, N, R, N, S, N, S, N, N, S }, // Bug
45
+ [_]Effectiveness{ N, N, R, I, N, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Ghost
46
46
  [_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
47
- [_]Effectiveness{ N, N, N, N, N, R, S, N, S, N, R, R, S, N, N, S, R, N }, // Fire
48
- [_]Effectiveness{ N, N, N, N, S, S, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
49
- [_]Effectiveness{ N, N, R, R, S, S, R, N, R, N, R, S, R, N, N, N, R, N }, // Grass
50
- [_]Effectiveness{ N, N, S, N, I, N, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
51
- [_]Effectiveness{ N, S, N, S, N, N, N, N, R, N, N, N, N, N, R, N, N, I }, // Psychic
52
- [_]Effectiveness{ N, N, S, N, S, N, N, N, R, N, R, R, S, N, N, R, S, N }, // Ice
53
- [_]Effectiveness{ N, N, N, N, N, N, N, N, R, N, N, N, N, N, N, N, S, N }, // Dragon
54
- [_]Effectiveness{ N, R, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Dark
47
+ [_]Effectiveness{ N, R, S, N, N, N, N, S, N, N, R, R, S, N, N, S, R, N }, // Fire
48
+ [_]Effectiveness{ S, S, N, N, N, N, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
49
+ [_]Effectiveness{ S, S, R, N, N, R, R, R, N, N, R, S, R, N, N, N, R, N }, // Grass
50
+ [_]Effectiveness{ I, N, N, N, N, S, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
51
+ [_]Effectiveness{ N, N, R, N, S, N, S, N, N, N, N, N, N, N, R, N, N, I }, // Psychic
52
+ [_]Effectiveness{ S, N, R, N, N, S, N, N, N, N, R, R, S, N, N, R, S, N }, // Ice
53
+ [_]Effectiveness{ N, N, R, N, N, N, N, N, N, N, N, N, N, N, N, N, S, N }, // Dragon
54
+ [_]Effectiveness{ N, N, R, N, R, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Dark
55
55
  };
56
56
 
57
57
  const PRECEDENCE = [_]u8{
58
+ 9, // Ground
59
+ 13, // Rock
60
+ 17, // Steel
58
61
  1, // Normal
59
62
  7, // Fighting
60
63
  10, // Flying
61
64
  8, // Poison
62
- 9, // Ground
63
- 13, // Rock
64
65
  12, // Bug
65
66
  14, // Ghost
66
- 17, // Steel
67
67
  0, // ???
68
68
  2, // Fire
69
69
  3, // Water
@@ -75,29 +75,126 @@ pub const Type = enum(u8) {
75
75
  16, // Dark
76
76
  };
77
77
 
78
+ const INDEXES = [_]u8{
79
+ 4, // Ground
80
+ 5, // Rock
81
+ 9, // Steel
82
+ 0, // Normal
83
+ 1, // Fighting
84
+ 2, // Flying
85
+ 3, // Poison
86
+ 7, // Bug
87
+ 8, // Ghost
88
+ 19, // ???
89
+ 20, // Fire
90
+ 21, // Water
91
+ 22, // Grass
92
+ 23, // Electric
93
+ 24, // Psychic
94
+ 25, // Ice
95
+ 26, // Dragon
96
+ 27, // Dark
97
+ };
98
+
99
+ const CONVERSION_2 = [_]Type{
100
+ .Normal,
101
+ .Fighting,
102
+ .Flying,
103
+ .Poison,
104
+ .Ground,
105
+ .Rock,
106
+ .@"???", // placeholder
107
+ .Bug,
108
+ .Ghost,
109
+ .Steel,
110
+ };
111
+
112
+ const HIDDEN_POWER = [_]Type{
113
+ .Fighting,
114
+ .Flying,
115
+ .Poison,
116
+ .Ground,
117
+ .Rock,
118
+ .Bug,
119
+ .Ghost,
120
+ .Steel,
121
+ .Fire,
122
+ .Water,
123
+ .Grass,
124
+ .Electric,
125
+ .Psychic,
126
+ .Ice,
127
+ .Dragon,
128
+ .Dark,
129
+ };
130
+
131
+ /// Order of Pokémon Showdown's types.
132
+ pub const SHOWDOWN = [_]Type{
133
+ .Fire,
134
+ .Ice,
135
+ .Steel,
136
+ .Electric,
137
+ .Ghost,
138
+ .Grass,
139
+ .Dark,
140
+ .Bug,
141
+ .Dragon,
142
+ .Fighting,
143
+ .Flying,
144
+ .Ground,
145
+ .Normal,
146
+ .Poison,
147
+ .Psychic,
148
+ .Rock,
149
+ .Water,
150
+ .@"???",
151
+ };
152
+
78
153
  comptime {
79
154
  assert(@bitSizeOf(Type) == 8);
80
155
  assert(@sizeOf(@TypeOf(CHART)) == 324);
81
156
  assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
157
+ assert(@sizeOf(@TypeOf(INDEXES)) == 18);
158
+ assert(@sizeOf(@TypeOf(CONVERSION_2)) == 10);
159
+ assert(@sizeOf(@TypeOf(HIDDEN_POWER)) == 16);
160
+ assert(@sizeOf(@TypeOf(SHOWDOWN)) == 18);
82
161
  }
83
162
 
84
163
  /// The number of types in this generation.
85
164
  pub const size = 18;
86
165
 
87
166
  /// Whether or not this type is considered to be special as opposed to physical.
88
- pub inline fn special(self: Type) bool {
167
+ pub fn special(self: Type) bool {
89
168
  return @intFromEnum(self) >= @intFromEnum(Type.Fire);
90
169
  }
91
170
 
92
171
  /// The `Effectiveness` of type `t2` vs. type `t1`.
93
- pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
172
+ pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
94
173
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
95
174
  }
96
175
 
97
- /// The precedence order of type type.
98
- pub inline fn precedence(self: Type) u8 {
176
+ /// The precedence order of Type `type`.
177
+ pub fn precedence(self: Type) u8 {
99
178
  return PRECEDENCE[@intFromEnum(self)];
100
179
  }
180
+
181
+ /// The internal index of this Type used by Present.
182
+ pub fn present(self: Type) u8 {
183
+ return INDEXES[@intFromEnum(self)];
184
+ }
185
+
186
+ /// The Type corresponding to a random roll of `num` for Conversion 2.
187
+ pub fn conversion2(num: u8) Type {
188
+ assert(num != 6);
189
+ assert(num < 10 or num >= 20);
190
+ assert(num <= 27);
191
+ return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
192
+ }
193
+
194
+ /// The Type corresponding to a Hidden Power `index`.
195
+ pub fn hiddenPower(index: u8) Type {
196
+ return HIDDEN_POWER[index];
197
+ }
101
198
  };
102
199
 
103
200
  /// Representation of a Pokémon's typing.
@@ -112,12 +209,18 @@ pub const Types = extern struct {
112
209
  }
113
210
 
114
211
  /// Whether this typing is immune to type `t`.
115
- pub inline fn immune(self: Types, t: Type) bool {
212
+ pub fn immune(self: Types, t: Type) bool {
116
213
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
117
214
  }
118
215
 
119
216
  /// Whether this typing includes type `t`.
120
- pub inline fn includes(self: Types, t: Type) bool {
217
+ pub fn includes(self: Types, t: Type) bool {
121
218
  return self.type1 == t or self.type2 == t;
122
219
  }
220
+
221
+ /// Whether this typing is immune to damage from Sandstorm.
222
+ pub fn sandstormImmune(self: Types) bool {
223
+ return @intFromEnum(self.type1) <= @intFromEnum(Type.Steel) or
224
+ @intFromEnum(self.type2) <= @intFromEnum(Type.Steel);
225
+ }
123
226
  };
@@ -51,6 +51,7 @@ const showdown = pkmn.options.showdown;
51
51
  pub const PRNG = rng.PRNG(2);
52
52
 
53
53
  pub fn Battle(comptime RNG: anytype) type {
54
+ const padding = if (!showdown) 3 else 5;
54
55
  return extern struct {
55
56
  const Self = @This();
56
57
 
@@ -59,20 +60,19 @@ pub fn Battle(comptime RNG: anytype) type {
59
60
  turn: u16 = 0,
60
61
  field: Field = .{},
61
62
 
62
- _1: u8 = 0,
63
- _2: if (!showdown) u16 else u32 = 0,
63
+ _: [padding]u8 = .{0} ** padding,
64
64
 
65
- pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
65
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
66
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
67
67
  return &self.sides[@intFromEnum(player)];
68
68
  }
69
69
 
70
- pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
70
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
71
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
72
72
  return &self.sides[@intFromEnum(player.foe())];
73
73
  }
74
74
 
75
- pub inline fn active(self: *const Self, player: Player) ID {
75
+ pub fn active(self: *const Self, player: Player) ID {
76
76
  return player.ident(@intCast(self.side(player).pokemon[0].position));
77
77
  }
78
78
 
@@ -106,13 +106,11 @@ pub const Side = extern struct {
106
106
  pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
107
107
  active: ActivePokemon align(8) = .{},
108
108
  conditions: Conditions = .{},
109
- _1: u32 = 0,
110
- _2: u16 = 0,
111
- // NB: wCurPlayerMove / wCurEnemyMove (?)
112
- _3: u8 = 0,
113
109
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
114
110
  last_used_move: Move = .None,
115
111
 
112
+ _: [7]u8 = .{0} ** 7,
113
+
116
114
  const Conditions = packed struct(u32) {
117
115
  Spikes: bool = false,
118
116
  Safeguard: bool = false,
@@ -135,7 +133,7 @@ pub const Side = extern struct {
135
133
  assert(@sizeOf(Side) == 248);
136
134
  }
137
135
 
138
- pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
139
137
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
140
138
  assert(slot > 0 and slot <= 6);
141
139
  const id = self.pokemon[slot - 1].position;
@@ -143,19 +141,19 @@ pub const Side = extern struct {
143
141
  return &self.pokemon[id - 1];
144
142
  }
145
143
 
146
- pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
144
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
147
145
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
148
146
  return self.get(1);
149
147
  }
150
148
 
151
- pub inline fn lastMove(self: anytype, encore: bool) Move {
149
+ pub fn lastMove(self: anytype, encore: bool) Move {
152
150
  assert(self.last_used_move != .None or !self.active.volatiles.dirty);
153
151
  if (encore) return self.last_used_move;
154
152
  return if (self.active.volatiles.dirty) .None else self.last_used_move;
155
153
  }
156
154
 
157
155
  // TODO
158
- // pub inline fn useMove(self: anytype) void {
156
+ // pub fn useMove(self: anytype) void {
159
157
  // assert(side.last_selected_move != .None);
160
158
  // self.last_used_move = side.last_selected_move;
161
159
  // self.active.volatiles.dirty = false;
@@ -177,7 +175,7 @@ pub const ActivePokemon = extern struct {
177
175
  assert(@sizeOf(ActivePokemon) == 44);
178
176
  }
179
177
 
180
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
178
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
181
179
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
182
180
  assert(mslot > 0 and mslot <= 4);
183
181
  assert(self.moves[mslot - 1].id != .None);
@@ -202,7 +200,7 @@ pub const Pokemon = extern struct {
202
200
  assert(@sizeOf(Pokemon) == 32);
203
201
  }
204
202
 
205
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
203
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
206
204
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
207
205
  assert(mslot > 0 and mslot <= 4);
208
206
  assert(self.moves[mslot - 1].id != .None);
@@ -224,10 +222,10 @@ pub const DVs = packed struct(u16) {
224
222
  pub fn init(g: Gender, p: u6, t: Type) DVs {
225
223
  assert(p >= 1 and p <= 40);
226
224
  assert(t != .Normal and t != .@"???");
227
- return DVs{ .gender = g, .pow = p, .type = t };
225
+ return .{ .gender = g, .pow = p, .type = t };
228
226
  }
229
227
 
230
- pub inline fn power(dvs: DVs) u8 {
228
+ pub fn power(dvs: DVs) u8 {
231
229
  return @as(u8, dvs.pow) + 30;
232
230
  }
233
231
 
@@ -241,9 +239,8 @@ pub const DVs = packed struct(u16) {
241
239
  };
242
240
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
243
241
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
244
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
245
- if (t >= @intFromEnum(Type.@"???")) t = t + 1;
246
- return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
242
+ var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
243
+ return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
247
244
  }
248
245
  };
249
246
 
@@ -282,14 +279,19 @@ pub const MoveSlot = extern struct {
282
279
 
283
280
  pub const Status = gen1.Status;
284
281
 
282
+ /// TODO
283
+ pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
284
+
285
285
  pub const Volatiles = packed struct(u128) {
286
286
  Bide: bool = false,
287
287
  Thrashing: bool = false,
288
+ BeatUp: bool = false,
288
289
  Flinch: bool = false,
289
290
  Charging: bool = false,
290
291
  Underground: bool = false,
291
292
  Flying: bool = false,
292
293
  Confusion: bool = false,
294
+
293
295
  Mist: bool = false,
294
296
  FocusEnergy: bool = false,
295
297
  Substitute: bool = false,
@@ -307,22 +309,28 @@ pub const Volatiles = packed struct(u128) {
307
309
  Endure: bool = false,
308
310
  Rollout: bool = false,
309
311
  Attract: bool = false,
312
+
310
313
  DefenseCurl: bool = false,
311
314
  Encore: bool = false,
312
315
  LockOn: bool = false,
313
316
  DestinyBond: bool = false,
314
- BeatUp: bool = false,
317
+ Trapped: bool = false,
315
318
 
316
- trapped: bool = false,
317
319
  switched: bool = false,
320
+ // TODO: replace with simply looking at Choice on Battle
318
321
  switching: bool = false,
322
+ // TODO: simply store both last moves on side
319
323
  dirty: bool = false,
320
324
 
321
- _: u20 = 0,
325
+ frozen: bool = false,
326
+ minimized: bool = false,
327
+ _: u10 = 0,
328
+ // TODO: is this needed for unmodified stats?
329
+ transform: u4 = 0,
322
330
 
323
- bind: u4 = 0,
324
331
  bide: u16 = 0,
325
- disabled: Disabled = .{},
332
+ bind: Bind = .{},
333
+ disable: Disable = .{},
326
334
  rage: u8 = 0,
327
335
  substitute: u8 = 0,
328
336
  toxic: u8 = 0,
@@ -334,7 +342,12 @@ pub const Volatiles = packed struct(u128) {
334
342
  protect: u4 = 0,
335
343
  attacks: u4 = 0, // bide/thrashing/rollout turns
336
344
 
337
- const Disabled = packed struct(u8) {
345
+ const Bind = packed struct(u8) {
346
+ duration: u4 = 0,
347
+ reason: u4 = 0,
348
+ };
349
+
350
+ const Disable = packed struct(u8) {
338
351
  move: u4 = 0,
339
352
  duration: u4 = 0,
340
353
  };
@@ -388,7 +401,7 @@ pub const Boosts = packed struct(u32) {
388
401
  };
389
402
 
390
403
  test Boosts {
391
- const boosts = Boosts{ .spd = -6 };
404
+ const boosts: Boosts = .{ .spd = -6 };
392
405
  try expectEqual(0, boosts.atk);
393
406
  try expectEqual(-6, boosts.spd);
394
407
  }
@@ -401,15 +414,10 @@ test Item {
401
414
  try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
402
415
  try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
403
416
 
404
- try expect(!Item.mail(.UpGrade));
405
- try expect(Item.mail(.FlowerMail));
406
- try expect(Item.mail(.MirageMail));
407
- try expect(!Item.mail(.TownMap));
408
-
409
- try expect(!Item.berry(.LightBall));
410
- try expect(Item.berry(.PSNCureBerry));
411
- try expect(Item.berry(.GoldBerry));
412
- try expect(!Item.berry(.MasterBall));
417
+ try expect(!Item.isMail(.UpGrade));
418
+ try expect(Item.isMail(.FlowerMail));
419
+ try expect(Item.isMail(.MirageMail));
420
+ try expect(!Item.isMail(.TownMap));
413
421
  }
414
422
 
415
423
  pub const Move = moves.Move;