@pkmn/engine 0.1.0-dev.736f7a8b → 0.1.0-dev.77410aff

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 (45) hide show
  1. package/README.md +36 -14
  2. package/build/pkg/addon.js.map +1 -1
  3. package/build/pkg/data/ids.json +12 -12
  4. package/build/pkg/data/layout.json +2 -2
  5. package/build/pkg/data/protocol.json +69 -12
  6. package/build/pkg/data.d.ts +3 -2
  7. package/build/pkg/data.js.map +1 -1
  8. package/build/pkg/gen1.js +2 -2
  9. package/build/pkg/gen1.js.map +1 -1
  10. package/build/pkg/index.d.ts +2 -1
  11. package/build/pkg/index.js.map +1 -1
  12. package/build/pkg/protocol.js +57 -38
  13. package/build/pkg/protocol.js.map +1 -1
  14. package/build/test/display.js.map +1 -1
  15. package/build/tools/debug.js +3 -1
  16. package/build/tools/debug.js.map +1 -1
  17. package/build.zig +40 -23
  18. package/package.json +30 -17
  19. package/src/bin/install-pkmn-engine +1 -1
  20. package/src/lib/binding/node.zig +7 -11
  21. package/src/lib/common/data.zig +8 -20
  22. package/src/lib/common/optional.zig +8 -1
  23. package/src/lib/common/protocol.zig +725 -299
  24. package/src/lib/common/rational.zig +12 -8
  25. package/src/lib/common/rng.zig +15 -6
  26. package/src/lib/gen1/README.md +31 -29
  27. package/src/lib/gen1/calc.zig +9 -9
  28. package/src/lib/gen1/chance.zig +55 -39
  29. package/src/lib/gen1/data/moves.zig +192 -180
  30. package/src/lib/gen1/data/species.zig +2 -2
  31. package/src/lib/gen1/data/types.zig +7 -7
  32. package/src/lib/gen1/data.zig +31 -43
  33. package/src/lib/gen1/helpers.zig +26 -41
  34. package/src/lib/gen1/mechanics.zig +314 -365
  35. package/src/lib/gen2/README.md +18 -72
  36. package/src/lib/gen2/calc.zig +143 -0
  37. package/src/lib/gen2/chance.zig +1314 -0
  38. package/src/lib/gen2/data/items.zig +12 -18
  39. package/src/lib/gen2/data/moves.zig +649 -568
  40. package/src/lib/gen2/data/species.zig +1 -1
  41. package/src/lib/gen2/data/types.zig +134 -31
  42. package/src/lib/gen2/data.zig +80 -68
  43. package/src/lib/gen2/generated.zig +806 -0
  44. package/src/lib/gen2/helpers.zig +4 -9
  45. package/src/lib/gen2/mechanics.zig +3238 -85
@@ -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,17 +60,19 @@ pub fn Battle(comptime RNG: anytype) type {
59
60
  turn: u16 = 0,
60
61
  field: Field = .{},
61
62
 
62
- pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
+ _: [padding]u8 = .{0} ** padding,
64
+
65
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
63
66
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
64
67
  return &self.sides[@intFromEnum(player)];
65
68
  }
66
69
 
67
- pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
70
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
68
71
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
69
72
  return &self.sides[@intFromEnum(player.foe())];
70
73
  }
71
74
 
72
- pub inline fn active(self: *const Self, player: Player) ID {
75
+ pub fn active(self: *const Self, player: Player) ID {
73
76
  return player.ident(@intCast(self.side(player).pokemon[0].position));
74
77
  }
75
78
 
@@ -83,18 +86,13 @@ pub fn Battle(comptime RNG: anytype) type {
83
86
  };
84
87
  }
85
88
 
86
- // TODO
87
89
  test Battle {
88
- try expectEqual(496, @sizeOf(Battle(PRNG)));
90
+ try expectEqual(512, @sizeOf(Battle(PRNG)));
89
91
  }
90
92
 
91
- pub const Field = packed struct {
93
+ pub const Field = packed struct(u8) {
92
94
  weather: Weather = .None,
93
95
  weather_duration: u4 = 0,
94
-
95
- comptime {
96
- assert(@sizeOf(Field) == 1);
97
- }
98
96
  };
99
97
 
100
98
  pub const Weather = enum(u4) {
@@ -106,14 +104,14 @@ pub const Weather = enum(u4) {
106
104
 
107
105
  pub const Side = extern struct {
108
106
  pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
109
- active: ActivePokemon = .{},
107
+ active: ActivePokemon align(8) = .{},
110
108
  conditions: Conditions = .{},
111
- // NB: wCurPlayerMove / wCurEnemyMove
112
- last_selected_move: Move = .None,
113
109
  // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
114
110
  last_used_move: Move = .None,
115
111
 
116
- const Conditions = packed struct {
112
+ _: [7]u8 = .{0} ** 7,
113
+
114
+ const Conditions = packed struct(u32) {
117
115
  Spikes: bool = false,
118
116
  Safeguard: bool = false,
119
117
  LightScreen: bool = false,
@@ -123,16 +121,19 @@ pub const Side = extern struct {
123
121
  light_screen: u4 = 0,
124
122
  reflect: u4 = 0,
125
123
 
126
- comptime {
127
- assert(@sizeOf(Conditions) == 2);
128
- }
124
+ future_sight: FutureSight = .{},
125
+
126
+ const FutureSight = packed struct(u16) {
127
+ damage: u12 = 0,
128
+ count: u4 = 0,
129
+ };
129
130
  };
130
131
 
131
132
  comptime {
132
- assert(@sizeOf(Side) == 240);
133
+ assert(@sizeOf(Side) == 248);
133
134
  }
134
135
 
135
- pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
137
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
137
138
  assert(slot > 0 and slot <= 6);
138
139
  const id = self.pokemon[slot - 1].position;
@@ -140,16 +141,23 @@ pub const Side = extern struct {
140
141
  return &self.pokemon[id - 1];
141
142
  }
142
143
 
143
- pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
144
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
144
145
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
145
146
  return self.get(1);
146
147
  }
147
148
 
148
- pub inline fn lastMove(self: anytype, encore: bool) Move {
149
+ pub fn lastMove(self: anytype, encore: bool) Move {
149
150
  assert(self.last_used_move != .None or !self.active.volatiles.dirty);
150
151
  if (encore) return self.last_used_move;
151
152
  return if (self.active.volatiles.dirty) .None else self.last_used_move;
152
153
  }
154
+
155
+ // TODO
156
+ // pub fn useMove(self: anytype) void {
157
+ // assert(side.last_selected_move != .None);
158
+ // self.last_used_move = side.last_selected_move;
159
+ // self.active.volatiles.dirty = false;
160
+ // }
153
161
  };
154
162
 
155
163
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
@@ -160,13 +168,14 @@ pub const ActivePokemon = extern struct {
160
168
  boosts: Boosts = .{},
161
169
  types: Types = .{},
162
170
  species: Species = .None,
163
- item: Item = .None,
171
+
172
+ _: u8 = 0,
164
173
 
165
174
  comptime {
166
175
  assert(@sizeOf(ActivePokemon) == 44);
167
176
  }
168
177
 
169
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
178
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
170
179
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
171
180
  assert(mslot > 0 and mslot <= 4);
172
181
  assert(self.moves[mslot - 1].id != .None);
@@ -191,7 +200,7 @@ pub const Pokemon = extern struct {
191
200
  assert(@sizeOf(Pokemon) == 32);
192
201
  }
193
202
 
194
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
203
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
195
204
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
196
205
  assert(mslot > 0 and mslot <= 4);
197
206
  assert(self.moves[mslot - 1].id != .None);
@@ -205,7 +214,7 @@ pub const Gender = enum(u2) {
205
214
  Unknown,
206
215
  };
207
216
 
208
- pub const DVs = packed struct {
217
+ pub const DVs = packed struct(u16) {
209
218
  gender: Gender = .Male,
210
219
  pow: u6 = 40,
211
220
  type: Type = .Dark,
@@ -213,10 +222,10 @@ pub const DVs = packed struct {
213
222
  pub fn init(g: Gender, p: u6, t: Type) DVs {
214
223
  assert(p >= 1 and p <= 40);
215
224
  assert(t != .Normal and t != .@"???");
216
- return DVs{ .gender = g, .pow = p, .type = t };
225
+ return .{ .gender = g, .pow = p, .type = t };
217
226
  }
218
227
 
219
- pub inline fn power(dvs: DVs) u8 {
228
+ pub fn power(dvs: DVs) u8 {
220
229
  return @as(u8, dvs.pow) + 30;
221
230
  }
222
231
 
@@ -230,13 +239,8 @@ pub const DVs = packed struct {
230
239
  };
231
240
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
232
241
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
233
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
234
- if (t >= @intFromEnum(Type.@"???")) t = t + 1;
235
- return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
236
- }
237
-
238
- comptime {
239
- assert(@sizeOf(DVs) == 2);
242
+ const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
243
+ return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
240
244
  }
241
245
  };
242
246
 
@@ -261,10 +265,12 @@ test DVs {
261
265
  try expectEqual(Gender.Female, dvs.gender);
262
266
  }
263
267
 
264
- pub const MoveSlot = packed struct {
268
+ /// Representation of a Generation II Pokémon's move slot in a battle.
269
+ pub const MoveSlot = extern struct {
270
+ /// The identifier for the move.
265
271
  id: Move = .None,
266
- pp_ups: u2 = 3,
267
- pp: u6 = 0,
272
+ /// The remaining PP of the move.
273
+ pp: u8 = 0,
268
274
 
269
275
  comptime {
270
276
  assert(@sizeOf(MoveSlot) == 2);
@@ -273,14 +279,19 @@ pub const MoveSlot = packed struct {
273
279
 
274
280
  pub const Status = gen1.Status;
275
281
 
276
- pub const Volatiles = packed struct {
282
+ /// TODO
283
+ pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
284
+
285
+ pub const Volatiles = packed struct(u128) {
277
286
  Bide: bool = false,
278
287
  Thrashing: bool = false,
288
+ BeatUp: bool = false,
279
289
  Flinch: bool = false,
280
290
  Charging: bool = false,
281
291
  Underground: bool = false,
282
292
  Flying: bool = false,
283
293
  Confusion: bool = false,
294
+
284
295
  Mist: bool = false,
285
296
  FocusEnergy: bool = false,
286
297
  Substitute: bool = false,
@@ -291,53 +302,55 @@ pub const Volatiles = packed struct {
291
302
  Transform: bool = false,
292
303
 
293
304
  Nightmare: bool = false,
294
- Cure: bool = false,
305
+ Curse: bool = false,
295
306
  Protect: bool = false,
296
307
  Foresight: bool = false,
297
308
  PerishSong: bool = false,
298
309
  Endure: bool = false,
299
310
  Rollout: bool = false,
300
311
  Attract: bool = false,
312
+
301
313
  DefenseCurl: bool = false,
302
314
  Encore: bool = false,
303
315
  LockOn: bool = false,
304
316
  DestinyBond: bool = false,
305
- BeatUp: bool = false,
317
+ Trapped: bool = false,
306
318
 
307
- trapped: bool = false,
308
319
  switched: bool = false,
320
+ // TODO: replace with simply looking at Choice on Battle
309
321
  switching: bool = false,
322
+ // TODO: simply store both last moves on side
310
323
  dirty: bool = false,
311
324
 
312
- _: u4 = 0,
325
+ frozen: bool = false,
326
+ minimized: bool = false,
327
+ _: u10 = 0,
328
+ // TODO: is this needed for unmodified stats?
329
+ transform: u4 = 0,
313
330
 
314
- bind: u4 = 0,
315
- future_sight: FutureSight = .{},
316
331
  bide: u16 = 0,
317
- disabled: Disabled = .{},
332
+ bind: Bind = .{},
333
+ disable: Disable = .{},
318
334
  rage: u8 = 0,
319
335
  substitute: u8 = 0,
320
336
  toxic: u8 = 0,
321
337
  confusion: u4 = 0,
322
338
  encore: u4 = 0,
339
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
323
340
  fury_cutter: u4 = 0,
324
341
  perish_song: u4 = 0,
325
342
  protect: u4 = 0,
326
- rollout: u4 = 0,
343
+ attacks: u4 = 0, // bide/thrashing/rollout turns
327
344
 
328
- const Disabled = packed struct {
329
- move: u4 = 0,
345
+ const Bind = packed struct(u8) {
330
346
  duration: u4 = 0,
347
+ reason: u4 = 0,
331
348
  };
332
349
 
333
- const FutureSight = packed struct {
334
- damage: u12 = 0,
335
- count: u4 = 0,
350
+ const Disable = packed struct(u8) {
351
+ move: u4 = 0,
352
+ duration: u4 = 0,
336
353
  };
337
-
338
- comptime {
339
- assert(@sizeOf(Volatiles) == 16);
340
- }
341
354
  };
342
355
 
343
356
  pub fn Stats(comptime T: type) type {
@@ -372,7 +385,7 @@ test Stats {
372
385
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
373
386
  }
374
387
 
375
- pub const Boosts = packed struct {
388
+ pub const Boosts = packed struct(u32) {
376
389
  atk: i4 = 0,
377
390
  def: i4 = 0,
378
391
  spe: i4 = 0,
@@ -388,7 +401,7 @@ pub const Boosts = packed struct {
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;
@@ -418,9 +426,13 @@ test Move {
418
426
  try expectEqual(251, @intFromEnum(Move.BeatUp));
419
427
  const move = Move.get(.DynamicPunch);
420
428
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
421
- try expectEqual(@as(u8, 50), move.accuracy);
422
- try expectEqual(@as(u8, 5), move.pp);
429
+ try expectEqual(rng.Gen12.percent(50), move.accuracy);
430
+ try expectEqual(rng.Gen12.percent(100), move.chance);
431
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
423
432
  try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
433
+ try expect(!move.effect.isHighCritical());
434
+ try expect(Move.get(.KarateChop).effect.isHighCritical());
435
+ try expect(Move.get(.RazorWind).effect.isHighCritical());
424
436
  }
425
437
 
426
438
  pub const Species = species.Species;