@pkmn/engine 0.1.0-dev.a02611a4 → 0.1.0-dev.a1fcc5d9

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