@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.cbdbb719

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